2.1. Tratamiento de valores perdidos o faltantes#
Introducción#
Un valor perdido o faltante es un valor nulo, que no representa ni la cadena vacía ni el valor 0. Como vimos en la teoría es un valor que puede tener un significado en sí mismo o no. Pandas utiliza diferentes valores para representar un valor faltante, que depende justamente del tipo de dato. Además, no todas las formas de representar nulos son puras de Pandas, sino que varias de ellas usan otras librerías.
Entonces, como en los tipos de datos, hay diferentes maneras de representar nulos.
Valores nulos en las librerías#
Valores null según la librería
Python nativo: se representan con
Noney significa la ausencia de un valor.NumPy: se representan con
np.nan(Not a Number). Se utiliza en conjuntos de datos numéricos.NaNes un valor de punto flotante que significa datos numéricos faltantes o indefinido. El significado específico de este valor lo podemos encontrar en la Norma IEEE 754Pandas: se representan con
pd.NA(Not Available) para nulos deboolean,Int64(8,16,32), ‘Float64ystrings, ypd.NaT`(Not a Time) para nulos de fechas/horas. La información completa de estos valores esta en Valores faltantes en Pandas
Tipo NaN (np.nan)
NaN (No es un número) es un valor de punto flotante que indica datos numéricos faltantes o indefinidos. NaN es que no es igual a nada, ni siquiera a sí mismo. Comparar NaN con el operador == siempre devuelve Falso. Según la norma IEEE 754 las operaciones aritméticas que involucran un NaN producen un NaN, permitiendo que este valor se propague a través de los cálculos.
A pesar de tener varias representaciones, las formas de identificar y trabajar con nulos es bastante simple.
Creamos un dataframe que tenga nulos de todos los tipos:
import pandas as pd
import numpy as np
df_con_nulos = pd.DataFrame({
"id": ["1", "2", np.nan, "4", None, pd.NA],
"precio": ["100.5", None, "300.1", "400", np.nan, 300],
"fecha": ["2024-01-01", "no válida", pd.NaT, "2024-01-04", None, "2025-03-15"],
"cliente": ["Ana", "Luis", "None", "Sofía", pd.NA, "Juan"],
"producto": ["Remera", None, "Campera", pd.NA, "Pantalon", "Camisa"],
"pagoCash": ["True", True, None, False, np.nan, pd.NA]
})
df_con_nulos
| id | precio | fecha | cliente | producto | pagoCash | |
|---|---|---|---|---|---|---|
| 0 | 1 | 100.5 | 2024-01-01 | Ana | Remera | True |
| 1 | 2 | None | no válida | Luis | None | True |
| 2 | NaN | 300.1 | NaT | None | Campera | None |
| 3 | 4 | 400 | 2024-01-04 | Sofía | <NA> | False |
| 4 | None | NaN | None | <NA> | Pantalon | NaN |
| 5 | <NA> | 300 | 2025-03-15 | Juan | Camisa | <NA> |
Vamos a analizar los tipos de datos de cada columna:
df_con_nulos.dtypes
id object
precio object
fecha object
cliente object
producto object
pagoCash object
dtype: object
Como podemos observar son todos object, es decir Pandas no pudo detectar el tipo de dato de cada uno.
Por ahora lo vamos a dejar así y vamos a analizar los métodos que existen para trabajar con estos valores.
Librerías para manejo de valores nulos#
Librerías para detectar y tratar nulos
Python nativo:
is None: verifica si un valor esNoney devuelveTrueoFalse. No es recomendable usar== NoneEl reemplazo de los valores nulos debe hacerse en forma manual, verificando con condicionales como:
if x is None
NumPy:
np.isnan(array): devuelve el mismo arreglo pero conTrueoFalsepor cada valor, dependiendo si esnano no. No realiza ninguna operación aritmética. Sólo funciona si tiene valores numéricos. No funciona conNone.np.nan_to_num(): reemplaza NaN con el valor numérico especificado (el valor predeterminado es 0). Por ejemplo,np.nan_to_num(arreglo, nan=-1, copy=False)reemplaza los valoresnandel arreglo por -1 modificando el mismo arreglo, es decir sin hacer una copia
Pandas:
pd.isna()/pd.isnull(): verifica si un valor es tantoNaN,NaToNonedentro de undataFrameoseriey devuelveTrueoFalsede la misma forma. Otra similar pero al revés espd.notnull()df.fillna(): reemplaza nulos, de las filas (axis=0) o columnas(axis=1), con el valor especificado. También se puede usardf.replace()que funciona en forma general para reemplazar cualquier valor. Si lo queremos hacer para los nulos, hacemosdf.replace(to_replace=np.nan, value=0)df.dropna(): elimina las filas que contienen nulos (NaNoNone). Devuelve un nuevodataframea no ser que sea indicadoinplace = Truedonde modifica el original. Conaxis=1elimina columnas
Nos vamos a centrar principalmente en los métodos de Pandas para trabajar con nulos.
MOMENTO CHEATSHEET
Este es el momento de recurrir a una nueva parte de la cheatsheet de Pandas para los nulos. En este caso que sólo describe dos de los métodos que describimos previamente.
Ahora que ya conocemos todas vamos a utilizar los métodos de Pandas.
Usando el
df_con_nulosanterior, probamos las funciones de verificación:
# volvemos al df creado
df_con_nulos
| id | precio | fecha | cliente | producto | pagoCash | |
|---|---|---|---|---|---|---|
| 0 | 1 | 100.5 | 2024-01-01 | Ana | Remera | True |
| 1 | 2 | None | no válida | Luis | None | True |
| 2 | NaN | 300.1 | NaT | None | Campera | None |
| 3 | 4 | 400 | 2024-01-04 | Sofía | <NA> | False |
| 4 | None | NaN | None | <NA> | Pantalon | NaN |
| 5 | <NA> | 300 | 2025-03-15 | Juan | Camisa | <NA> |
# usamos el método de pandas para verificar que valores nulos hay
df_con_nulos.isna()
| id | precio | fecha | cliente | producto | pagoCash | |
|---|---|---|---|---|---|---|
| 0 | False | False | False | False | False | False |
| 1 | False | True | False | False | True | False |
| 2 | True | False | True | False | False | True |
| 3 | False | False | False | False | True | False |
| 4 | True | True | True | True | False | True |
| 5 | True | False | False | False | False | True |
Métodos .fillna() y .dropna()#
Para reemplazar los valores nulos podemos usar el método fillna().
Método .fillna() para tratar nulos
Algunos de los parámetros mas importantes del método .fillna() son:
value: especifica el valor con el que se reemplazarán los valoresNaNy ‘NA. Puede ser un valor escalar, undictionary, o unaserie. La estructura debe coincidir con la cantidad deNaN`.axis: indica si el reemplazo se debe realizar a lo largo de las filas (axis = 0) o de las columnas (axis = 1). Por defecto, se establece en 0 (filas).inplace: determina si los cambios se deben realizar directamente sobre eldataframeoriginal.Truesignifica que se modificará el original, mientras queFalse(predeterminado) devolverá un nuevodataframecon los reemplazos.
También existen dos métdods que permiten rellenar nulos:
ffil()(forward fill): Rellena los valores faltantesnp.nanypd.NAhacia abajo usando el valor anteriorbfill()(backward fill): Rellena hacia arriba usando el valor siguiente
La documentación completa del método, la podemos encontrar en pandas.DataFrame.fillna
Vamos a llenar los nulos de la columna id:
# hago una copia, con copy para que sea una nueva instancia
df_sin_id_nulos = df_con_nulos.copy()
#completo los nulos con el valor 0 y creo un nuevo DF
df_sin_id_nulos["id"] = df_sin_id_nulos["id"].fillna(0)
df_sin_id_nulos
| id | precio | fecha | cliente | producto | pagoCash | |
|---|---|---|---|---|---|---|
| 0 | 1 | 100.5 | 2024-01-01 | Ana | Remera | True |
| 1 | 2 | None | no válida | Luis | None | True |
| 2 | 0 | 300.1 | NaT | None | Campera | None |
| 3 | 4 | 400 | 2024-01-04 | Sofía | <NA> | False |
| 4 | 0 | NaN | None | <NA> | Pantalon | NaN |
| 5 | 0 | 300 | 2025-03-15 | Juan | Camisa | <NA> |
# vamos a reemplazar los nulos del precio por el valor "sin precio" y "nose"
# como vemos en la columna precio hay un None en el indice 1
# y un NaN en el indice 4
# entonces la serie debe tener lo que quiero en esos mismos indices
# creo un nuevo DF
df_sin_id_precio_nulos = df_sin_id_nulos.copy()
#reemplazo
df_sin_id_precio_nulos["precio"] = df_sin_id_precio_nulos["precio"].fillna(pd.Series([0,"sin precio",0,0,"nose",0]))
df_sin_id_precio_nulos
| id | precio | fecha | cliente | producto | pagoCash | |
|---|---|---|---|---|---|---|
| 0 | 1 | 100.5 | 2024-01-01 | Ana | Remera | True |
| 1 | 2 | sin precio | no válida | Luis | None | True |
| 2 | 0 | 300.1 | NaT | None | Campera | None |
| 3 | 4 | 400 | 2024-01-04 | Sofía | <NA> | False |
| 4 | 0 | nose | None | <NA> | Pantalon | NaN |
| 5 | 0 | 300 | 2025-03-15 | Juan | Camisa | <NA> |
Ahora vamos a eliminar las filas que tengan nulos:
# creo un nuevo df
df_sin_nulos = df_sin_id_precio_nulos.copy()
#borro todas las filas que tengan al menos un nulo
df_sin_nulos.dropna(inplace=True)
df_sin_nulos
| id | precio | fecha | cliente | producto | pagoCash | |
|---|---|---|---|---|---|---|
| 0 | 1 | 100.5 | 2024-01-01 | Ana | Remera | True |
y ahora las columnas que tengan nulos:
# creo un nuevo df
df_sin_nulos = df_sin_id_precio_nulos.copy()
#borro todas las columnas que tengan al menos un nulo
df_sin_nulos.dropna(inplace=True, axis = 1)
df_sin_nulos
| id | precio | |
|---|---|---|
| 0 | 1 | 100.5 |
| 1 | 2 | sin precio |
| 2 | 0 | 300.1 |
| 3 | 4 | 400 |
| 4 | 0 | nose |
| 5 | 0 | 300 |