Manejar datos nulos#

Los datos nulos corresponden a información que no se consignó, ya fuese en la creación del archivo o en la entrada de datos. Nuevamente, esta es una decisión de diseño del modelo de datos y no todos los casos son iguales, así que hay que tener en cuenta que no todos los conjuntos de datos requerirán las mismas acciones.

dropna()#

Esta función elimina los valores nulos de un conjunto de datos. Su sintaxis es:

df.dropna()

Por ejemplo, si la aplicamos a los datos de muestra_covid tendremos el siguiente resultado:

sin_nulos = muestra_covid.dropna()
print(sin_nulos.shape)
sin_nulos.head()
(783, 12)
Unnamed: 0 sexo edad entidad_nacimiento municipio_residencia indigena nacionalidad migrante pais_nacionalidad fecha_ingreso fecha_sintomas fecha_def
337 337 HOMBRE 84 OAXACA nezahualcóyotl NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-06 2021-12-24 2022-01-14
1248 1248 MUJER 71 HIDALGO tlanepantla NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-01 2021-12-27 2022-01-12
1249 1249 MUJER 79 MÉXICO nezahualcóyotl NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-01 2021-12-25 2022-01-07
2305 2305 HOMBRE 87 MÉXICO ecatepec de morelos NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-02 2021-12-23 2022-01-03
2478 2478 HOMBRE 34 MÉXICO tlalnepantla de baz NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-02 2021-12-30 2022-01-03

Ahora tenemos un conjunto de datos más sintetizado. Demasiado sintético diría yo. El inconveniente con usar este método de manera simple es que cualquier fila que contenga un valor nulo será eliminada. Por lo tanto, solamente tendremos 783 casos en los que toda la información es válida.

Una solución menos “radical” podría ser eliminar los valores nulos solamente de una columna, por ejemplo, municipio_residencia:

sin_nulos = muestra_covid.dropna(subset=['municipio_residencia'])
print(sin_nulos.shape)
sin_nulos.head()
(149707, 12)
Unnamed: 0 sexo edad entidad_nacimiento municipio_residencia indigena nacionalidad migrante pais_nacionalidad fecha_ingreso fecha_sintomas fecha_def
7 7 MUJER 75 QUERÉTARO naucalpan de juárez NO MEXICANA NO ESPECIFICADO MÉXICO 2022-02-21 2022-02-16 NaN
10 10 HOMBRE 40 CIUDAD DE MÉXICO atizapán NO MEXICANA NO ESPECIFICADO MÉXICO 2022-05-19 2022-05-19 NaN
23 23 MUJER 9 CIUDAD DE MÉXICO zumpango NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-12 2022-01-07 NaN
24 24 MUJER 40 GUERRERO zumpango NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-12 2022-01-08 NaN
25 25 MUJER 13 CIUDAD DE MÉXICO nezahualcóyotl NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-26 2022-01-26 NaN

En este caso tendremos un número un tanto más significativo de casos (149,707).

Aparentemente hemos perdido mucha información, pero en realidad hemos segmentado el conjunto de datos para seleccionar solamente aquellos que podamos representar en un mapa. Como conservamos nuestro conjunto de datos original, incluso nuestro dataframe segmentado (muestra_covid), no debemos preocuparnos por perder la información de sexo, edad, etc; que no esté asociada a una ubicación geográfica.

fillna()#

Otra función para lidiar con valores nulos es fillna(). Esta función reemplaza los valores nulos por un valor específico. Su sintaxis es:

df.fillna(valor)

Ciertas operaciones en pandas no pueden lidiar con datos nulos, pero no siempre (como vimos arriba) podemos eliminar esos campos. Por esa razón, esta función permite llenar estos campos con un valor constante con el que podremos trabajar posteriormente.

Una aclaración

Aunque podemos asignar cualquier valor a fillna() es importante que seamos coherentes con el tipo de dato de la columna que estamos transformando. Por ejemplo, si la columna fecha_diagnostico es de tipo int64, entonces fillna() debe ser de tipo int64.

Además, en todos los casos, el valor debe ser similar a un valor nulo. Por ejemplo, 0 o 'N/A' son valores que pueden reemplazar a un valor nulo.

Al igual que en dropna() si usamos un valor indistintamente de la columna, se reemplazarán todos los valores nulos de todo el dataframe:

nulos_reemplazados = muestra_covid.fillna('N/A')
nulos_reemplazados.head()
Unnamed: 0 sexo edad entidad_nacimiento municipio_residencia indigena nacionalidad migrante pais_nacionalidad fecha_ingreso fecha_sintomas fecha_def
0 0 HOMBRE 43 CIUDAD DE MÉXICO N/A NO MEXICANA NO ESPECIFICADO MÉXICO 2022-05-03 2022-05-03 N/A
1 1 HOMBRE 39 CIUDAD DE MÉXICO N/A NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-13 2022-01-10 N/A
2 2 HOMBRE 55 CIUDAD DE MÉXICO N/A NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-12 2022-01-12 N/A
3 3 HOMBRE 54 CIUDAD DE MÉXICO N/A NO MEXICANA NO ESPECIFICADO MÉXICO 2022-02-20 2022-02-13 N/A
4 4 MUJER 41 CIUDAD DE MÉXICO N/A NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-12 2022-01-10 N/A

En este caso, el reemplazo en municipio_residencia es claro, pero en fecha_def puede llevar a errores (por ejemplo, cuando tratemos de cambiar los valores de fecha_def a datetime64[ns]). Otro inconveniente es que no es posible establecer una fecha 0 (recuerda lo que dijimos al respecto en la sección dedicada a variables cuantitativas y cualitativas sobre las escalas de intervalo). Por lo que en este caso, la decisión más adecuada podría ser reemplazar únicamente los valores nulos de la columna municipio_residencia con 'NO APLICA'.

nulos_reemplazados = muestra_covid.fillna({'municipio_residencia': 'NO APLICA'}) # usamos 'NO APLICA' como valor pues es el que se utiliza en el dataset original para otras categorías
nulos_reemplazados.head()
Unnamed: 0 sexo edad entidad_nacimiento municipio_residencia indigena nacionalidad migrante pais_nacionalidad fecha_ingreso fecha_sintomas fecha_def
0 0 HOMBRE 43 CIUDAD DE MÉXICO NO APLICA NO MEXICANA NO ESPECIFICADO MÉXICO 2022-05-03 2022-05-03 NaN
1 1 HOMBRE 39 CIUDAD DE MÉXICO NO APLICA NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-13 2022-01-10 NaN
2 2 HOMBRE 55 CIUDAD DE MÉXICO NO APLICA NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-12 2022-01-12 NaN
3 3 HOMBRE 54 CIUDAD DE MÉXICO NO APLICA NO MEXICANA NO ESPECIFICADO MÉXICO 2022-02-20 2022-02-13 NaN
4 4 MUJER 41 CIUDAD DE MÉXICO NO APLICA NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-12 2022-01-10 NaN

¿Cómo saber cuáles columnas tienen valores nulos?#

En conjuntos grandes de datos, es posible que no identifiquemos un valor nulo hasta el momento que nos encontremos con un mensaje de error. Por esta razón, es probable que queramos obtener las columnas que tengan valores nulos antes de proceder con un dataframe. Para ello, podemos usar la función isnull() aplicada a una columna en específico, combinada con el método .loc:

es_nula = muestra_covid.loc[muestra_covid['fecha_def'].isnull()]
es_nula
Unnamed: 0 sexo edad entidad_nacimiento municipio_residencia indigena nacionalidad migrante pais_nacionalidad fecha_ingreso fecha_sintomas fecha_def
0 0 HOMBRE 43 CIUDAD DE MÉXICO NaN NO MEXICANA NO ESPECIFICADO MÉXICO 2022-05-03 2022-05-03 NaN
1 1 HOMBRE 39 CIUDAD DE MÉXICO NaN NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-13 2022-01-10 NaN
2 2 HOMBRE 55 CIUDAD DE MÉXICO NaN NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-12 2022-01-12 NaN
3 3 HOMBRE 54 CIUDAD DE MÉXICO NaN NO MEXICANA NO ESPECIFICADO MÉXICO 2022-02-20 2022-02-13 NaN
4 4 MUJER 41 CIUDAD DE MÉXICO NaN NO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-12 2022-01-10 NaN
... ... ... ... ... ... ... ... ... ... ... ... ...
1323496 1323496 HOMBRE 48 CIUDAD DE MÉXICO NaN NO ESPECIFICADO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-05 2022-01-01 NaN
1323497 1323497 HOMBRE 27 CIUDAD DE MÉXICO NaN NO ESPECIFICADO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-04 2022-01-01 NaN
1323498 1323498 MUJER 46 CIUDAD DE MÉXICO NaN NO ESPECIFICADO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-05 2022-01-01 NaN
1323499 1323499 MUJER 33 CIUDAD DE MÉXICO NaN NO ESPECIFICADO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-05 2022-01-01 NaN
1323500 1323500 HOMBRE 34 MÉXICO NaN NO ESPECIFICADO MEXICANA NO ESPECIFICADO MÉXICO 2022-01-27 2022-01-27 NaN

1319912 rows × 12 columns

Puedes combinar este método con un loop para obtener una lista de las columnas que contienen valores nulos:

nulas = []
for col in muestra_covid:
    if muestra_covid[col].isnull().any(): # la función `any()` devuelve `True` si alguna de las filas contiene un valor nulo
        nulas.append(col)
nulas
['municipio_residencia', 'fecha_def']

Este loop lo puedes aplicar a cualquier dataframe, sin importar la cantidad de columnas que tenga.