Ir al contenido
wannacode

Limpieza de datos

En resumen: Limpiar los datos es quitar los duplicados, llevar precios, fechas y nombres a un solo formato y comprobar con código que los datos son correctos después de la limpieza.

Problema

La nueva lista de precios

Un proveedor nuevo ha enviado su lista de precios como CSV. El gerente pregunta dos cosas: cuánto pagaremos en promedio por producto y cuánto nos cuesta el café. El archivo supplier.csv está junto al código. Aquí está:

Pythondatos: las tablas de la tienda

Las cuatro columnas son str. Los precios son «0,95 €» (el proveedor escribe los precios a la manera continental, con coma decimal), «1.45», vacío y «a consultar». Las fechas están en dos formatos. El queso blanco está dos veces. Los nombres tienen mayúsculas, minúsculas y espacios. El café cuesta 1190.

prices['price'].mean() ni siquiera arranca: los precios son texto. A los datos así se les llama sucios, y son la norma, no la excepción. El programa del proveedor escribe lo que escribe, y las personas rellenan cosas a mano.

La idea

Limpiar es código, no trabajo manual

La tentación es abrir el archivo y corregir las celdas a mano. Pero dentro de un mes el proveedor enviará una lista nueva con los mismos errores. Por eso limpiar es código: se ejecuta de nuevo con cada archivo nuevo.

El camino es siempre el mismo:

  1. Mirar qué está mal: dtypes, describe, value_counts, ordenar.
  2. Corregir con un paso para cada tipo de suciedad: duplicados, texto, números, fechas, valores atípicos.
  3. Comprobar con código que ha funcionado: el número de filas antes y después, NaN solo donde debe haberlos.

Cada paso de esta lección es para uno de los tipos de suciedad de la lista.

La idea

Duplicados: duplicated y drop_duplicates

duplicated() da una máscara: True para cada fila que repite una fila de más arriba. .sum() las cuenta. Con subset= compara solo las columnas indicadas, y keep=False marca todas las copias, no solo la segunda:

Pythondatos: las tablas de la tienda

Hay una fila exactamente igual: el queso blanco, P07. Según el código, sin embargo, hay dos repeticiones. La leche fresca, P05, está dos veces, una como «LECHE FRESCA» y otra como « Leche fresca», con un espacio delante. Para pandas son textos distintos, así que duplicated() no lo detecta.

drop_duplicates() quita las repeticiones y se queda con la primera fila. Pero si lo ejecutas ahora, la leche sigue dos veces.

La idea

Nombres: str.strip y str.lower

El texto se limpia con .str, de la lección sobre tipos. str.strip() quita los espacios de los extremos, y str.lower() pone todas las letras en minúscula. Así «LECHE FRESCA» y « Leche fresca» pasan a ser el mismo texto:

Pythondatos: las tablas de la tienda

De 16 filas quedan 14: se han quitado el queso y la leche. El orden de los pasos importa: primero limpia el texto y después busca duplicados.

Más precisamente, para curiosos

str.lower() sirve para comparar. Para una etiqueta o un informe, str.capitalize() pone la primera letra en mayúscula: «leche fresca» pasa a «Leche fresca». Si los nombres también se diferencian en el medio (dos espacios entre palabras), str.replace(' ', ' ') o str.split().str.join(' ') los igualan.

Pruébalo

Sin repeticiones

Limpia los nombres de la lista (sin espacios en los extremos, en minúsculas), quita las filas repetidas y guarda en rows cuántas filas quedan.

Pythondatos: las tablas de la tienda

La idea

Precios a partir de texto: str.replace y pd.to_numeric

«0,95 €» es texto por tres motivos: el signo €, la coma en lugar del punto y el espacio. str.replace quita cada uno. Después pd.to_numeric convierte el texto en número. Con errors='coerce', lo que aún no sea un número se vuelve NaN en lugar de detener el código:

Pythondatos: las tablas de la tienda

El precio es float64, y hay dos NaN: las manzanas (un campo vacío) y el agua mineral («a consultar»). Eso es correcto: el proveedor no dio precio para ellos. Sin errors='coerce', to_numeric se detiene con ValueError: Unable to parse string "a consultar".

errors='coerce' es cómodo y peligroso: no dice qué ha tirado. Por eso cuentas los NaN antes y después. Si tras la conversión hay más que campos vacíos, algo se ha vuelto NaN por error.

Pruébalo

Precios como números

Convierte la columna price en número. Debe quedar NaN solo donde el proveedor no dio precio.

Pythondatos: las tablas de la tienda

Míralo

Fechas en dos formatos

La mitad de las fechas son «01.11.2026» (día, mes, año), y la otra mitad «2026-11-01». Si dejas que pandas adivine:

Pythondatos: las tablas de la tienda

pandas adivina el formato por la primera fecha y la lee como %m.%d.%Y: primero el mes, a la estadounidense. Con la fecha ISO se detiene con un error. Si todas las fechas tuvieran puntos, no habría error: 01.11 se convertiría en silencio en el 11 de enero.

Por eso indicas tú el formato, con format=, una vez para cada formato. Con errors='coerce' las fechas del otro formato se vuelven NaT, y fillna une las dos lecturas:

Pythondatos: las tablas de la tienda

13 filas valen desde el 1 de noviembre y 2 desde el 15 de noviembre. Una fecha no se puede leer con ninguno de los dos formatos: el 31.11.2026 de las bananas. Ese día no existe, noviembre tiene 30 días.

1. Después de las dos lecturas de las fechas, 31.11.2026 sigue siendo NaT. ¿Qué haces con la fila de las bananas?

Míralo

Valores atípicos: describe y ordenar

Los precios ya son números. describe() del primer vistazo te dice si parecen plausibles, y ordenar muestra cuáles son los mayores:

Pythondatos: las tablas de la tienda

La media es 102,70 €, y la mediana 1,62 €. Una diferencia así significa que un valor está lejos de los demás. Eso se llama valor atípico (outlier). Al ordenar se encuentra enseguida: el café en grano a 1190 €. Nosotros vendemos el café a 14,90 €, así que el precio del proveedor es 11,90 €: su programa perdió la coma decimal.

La corrección es una regla para todos los precios por encima de 100 €. La escribes en el código, no en una celda, para que detecte el mismo error también en la próxima lista:

Pythondatos: las tablas de la tienda

El café cuesta 11,90 €, la media bajó a 4,53 € y la mediana se quedó en 1,62 €. La mediana no nota un solo número enorme, y la media lo nota entero.

Más precisamente, para curiosos

La regla «más de 100 € significa coma perdida» es correcta para esta lista porque conocemos los productos: nada en ella cuesta más de 100 €. En otra lista (televisores, muebles) la misma regla estropearía precios correctos. Cuando no sabes por qué un valor es raro, pregunta de dónde viene en lugar de corregirlo.

La idea

Comprobaciones como código: assert

La limpieza está terminada cuando puedes demostrarlo. assert condición, 'mensaje' no hace nada si la condición es verdadera, y detiene el código con un AssertionError y el mensaje si no lo es. Aquí está toda la limpieza con las comprobaciones al final:

Pythondatos: las tablas de la tienda

De 16 filas quedan 14. Las comprobaciones dicen en voz alta lo que esperas:

  • cada código aparece una vez;
  • falta un precio solo en los dos productos sin precio;
  • cada precio está entre 0,10 € y 100 €;
  • solo queda una fecha sin leer.

Cuando llegue la próxima lista con un error nuevo, la comprobación detendrá el código con un mensaje claro, en lugar de que el informe salga mal en silencio.

Ejercicio

El precio medio del proveedor

Responde al gerente. Limpia toda la lista en prices: los nombres y los duplicados, los precios como números, las fechas en los dos formatos y el café sin su coma decimal. Los productos sin precio se quedan, con NaN. Guarda en avg_price el precio medio, redondeado a 2 decimales.

Pythondatos: las tablas de la tienda

Test

Ponte a prueba

1. La leche fresca está dos veces en la lista: LECHE FRESCA y Leche fresca. ¿Cuántos duplicados encuentra drop_duplicates() antes de limpiar los nombres?
2. ¿Por qué pd.to_numeric(prices['price'], errors='coerce') sin nada más es peligroso?
3. El precio medio es 102,70 €, y la mediana es 1,62 €. ¿Qué te dice eso?
4. ¿Qué es mejor después de limpiar?

Creado por Ivo Gergov como parte de la campaña de Evo Tech por una educación accesible en programación ·