Ir al contenido
wannacode

Tipos y valores faltantes

En resumen: Cada columna en pandas tiene un tipo (dtype) que decide qué puedes hacer con ella, y los valores faltantes (NaN) se encuentran con isna y se rellenan con fillna o se eliminan con dropna.

Problema

«¿Qué día se pide más?»

El gerente quiere saber cuándo poner más gente en el almacén. El archivo year_orders.csv tiene 11 116 pedidos de un año, cada uno con fecha y hora. Parece un solo comando: tomar el día de la fecha y contar.

Pythondatos: las tablas de la tienda

Un error. dtypes dice por qué: ordered_at es str, texto. Para nosotros «2025-11-01 07:20» es una fecha, pero read_csv ve dígitos, guiones y dos puntos, y no adivina.

El tipo de una columna decide qué puedes hacer con ella. En esta lección verás cómo cambiarlo, qué tipos ahorran memoria y dan un orden, y qué pasa cuando falta un valor.

La idea

Cada columna tiene un tipo: astype

Cada columna de un DataFrame tiene un tipo (dtype). Has visto int64 y float64 para números y str para texto. También existen bool para True y False, datetime64 para fechas y category para unos pocos valores que se repiten.

read_csv adivina el tipo a partir del texto del archivo. Cuando adivina mal, o cuando necesitas otro, astype hace una columna de un tipo nuevo. En products la categoría es float64, porque la tarjeta de regalo no tiene categoría, y NaN es un número decimal. Los enteros con huecos los guarda el tipo Int64 (con I mayúscula):

Pythondatos: las tablas de la tienda

Los números vuelven a ser enteros, y el hueco es <NA>: el signo para «nada» en los tipos más nuevos de pandas. Con astype(int) obtendrías el error IntCastingNaNError, porque un int normal no tiene sitio para un hueco.

1. prices guarda los precios de los cuatro primeros productos: 1.2, 1.9, 0.8 y 2.5. ¿Qué da prices.astype(int)?

Míralo

Pocos valores, muchas filas: category

La columna status tiene 11 116 filas pero solo cinco palabras distintas. Como texto, cada palabra se guarda en cada fila. El tipo category guarda las cinco palabras una vez, y para cada fila solo el número de su palabra:

Pythondatos: las tablas de la tienda

377 057 bytes pasan a 11 345, unas 33 veces menos. En el año de la tienda es poco, pero en una tabla con millones de filas es la diferencia entre «cabe en la memoria» y «no cabe».

Una categoría tiene también un segundo uso: un orden. Los estados no son un alfabeto sino un camino: new, paid, shipped, delivered. CategoricalDtype con ordered=True fija ese orden:

Pythondatos: las tablas de la tienda

value_counts(sort=False) cuenta en el orden del camino, no por cantidad. La comparación < 'delivered' significa «antes de la entrega»: 205 pedidos siguen en camino. Los cancelados se ponen al final, fuera del camino.

Pruébalo

Los estados a lo largo del camino

Convierte la columna status en una categoría con el orden new, paid, shipped, delivered, cancelled, y guarda en status_counts cuántos pedidos hay en cada paso, en ese orden.

Pythondatos: las tablas de la tienda

La idea

Fechas: pd.to_datetime y .dt

pd.to_datetime convierte el texto en fechas. La columna recibe el tipo datetime64 y ahora conoce el calendario. Las partes de una fecha salen de .dt: .dt.year, .dt.month, .dt.day_name() y muchas más:

Pythondatos: las tablas de la tienda

El año va del 1 de noviembre de 2025, 7:20, al 31 de octubre de 2026, 23:01. El primer pedido es un sábado (Saturday: day_name() da los nombres de los días en inglés). .dt funciona solo con fechas: con el texto del principio dio el error.

Más sobre fechas (por semana, por mes, una media móvil) en la lección sobre el tiempo.

1. when es una columna de fechas. El primer pedido es del 1 de noviembre de 2025, un sábado. ¿Qué da when.dt.day para él?

La idea

Texto: .str

Lo que .dt es para las fechas, .str lo es para el texto: aplica un método de cadena a cada fila de una vez. Viste str.contains con los filtros. También existen str.lower() y str.upper(), str.strip() (quita los espacios de los extremos), str.len(), str.split() y el corte como en una cadena: str[:18].

Las etiquetas de los estantes admiten 18 caracteres. ¿Qué nombres no caben, y cómo quedarán cortados?

Pythondatos: las tablas de la tienda

4 nombres son más largos que 18 caracteres. str[:18] los corta, y str.upper() los pone en mayúsculas para la etiqueta. El resultado vuelve a ser una columna, así que assign la pone junto al nombre.

La idea

Valores faltantes: isna y notna

Casi toda tabla real tiene algo que falta: un cliente no dio su ciudad, la caja no mandó un informe. pandas escribe el hueco como NaN (NaT para fechas, <NA> para los tipos más nuevos). isna() da una máscara que es True donde falta un valor, y .sum() sobre ella cuenta los huecos de cada columna:

Pythondatos: las tablas de la tienda

Un cliente no tiene ciudad. notna() es la máscara contraria: True donde hay un valor. Filtras con las dos máscaras como con cualquier condición.

Míralo

Por qué 0 no es «faltante»

La banitsa y los pepinos tienen stock 0. ¿Es un hueco?

Pythondatos: las tablas de la tienda

isna() no encuentra nada en stock: los ceros son valores reales. 0 significa «sabemos que no hay», y NaN significa «no lo sabemos».

Ahora los ingresos por día de la lección de resúmenes, pero como Serie de pandas. Una diferencia importante: NumPy daba nan para la media, y pandas se salta los huecos:

Pythondatos: las tablas de la tienda

count() cuenta 6 días conocidos de 7. Su media es 494,70 €, como con np.nanmean. La suma, 2968,20 €, deja fuera en silencio el miércoles, eso sí: pandas no te avisa. Con fillna(0) la media baja a 424,03 €, porque el cero dice «el miércoles no vendimos nada», y eso no es verdad.

Más precisamente, para curiosos

El salto lo controla el parámetro skipna: revenue.mean(skipna=False) da nan, como NumPy. Es útil cuando prefieres que el resultado señale un hueco en lugar de dar en silencio un número más pequeño.

La idea

fillna, dropna o nada

Hay tres maneras de tratar un hueco, y la elección depende de la pregunta, no del código:

  • fillna(valor), cuando sabes lo que significa el hueco. En un informe de «clientes por ciudad», una ciudad que falta es «desconocida»: es una respuesta, no una invención.
  • dropna(), cuando la fila no puede responder a la pregunta. En un informe por categoría, la tarjeta de regalo no tiene adónde ir. subset=[...] mira solo las columnas indicadas.
  • Nada, cuando no lo sabes. Los ingresos del miércoles no son 0, y la media de los días conocidos es más honesta.
Pythondatos: las tablas de la tienda

dropna quitó la tarjeta de regalo: de 25 productos quedan 24, tantos como cuenta notna() en la categoría. En un informe de «cuántos productos vendemos» eso sería un error. fillna('desconocida') convierte la ciudad que falta en una fila propia en value_counts.

Pruébalo

Clientes por ciudad

Marketing quiere saber de dónde son los clientes. Guarda en missing_city cuántos clientes no tienen ciudad, y en by_city cuántos clientes hay en cada ciudad, contando la ciudad que falta como «desconocida».

Pythondatos: las tablas de la tienda

Ejercicio

El día con más pedidos

Responde al gerente. Convierte ordered_at en una fecha, pon el nombre del día de la semana en una columna nueva day, guarda en by_day cuántos pedidos hay en cada día, y en best_day el día con más pedidos.

Pythondatos: las tablas de la tienda

Test

Ponte a prueba

1. La columna ordered_at se leyó de un CSV y tiene el tipo str. ¿Por qué orders['ordered_at'].dt.month da un error?
2. La columna status tiene 11 116 filas pero solo 5 valores distintos. ¿Qué ganas con astype('category')?
3. En los ingresos por día, un día es NaN. ¿Qué da revenue.mean() en pandas?
4. Dos productos tienen stock 0, y uno tiene categoría NaN. ¿Cuál de ellos es «faltante»?
5. Estás haciendo un informe de «cuántos productos vendemos», y la tarjeta de regalo no tiene categoría. ¿Qué haces con ella?

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