Manejo de valores NaN#
Al analizar datos tabulares, a veces hay celdas de tabla que no contienen datos. En Python, esto típicamente significa que el valor es Not a Number (NaN). No podemos asumir que estos valores son 0 o -1 o cualquier otro valor porque eso distorsionaría las estadísticas descriptivas, por ejemplo. Necesitamos tratar estas entradas NaN de manera diferente y este cuaderno introducirá cómo hacerlo.
Para obtener una primera vista de dónde los NaN juegan un papel, cargamos nuevamente una tabla de ejemplo y la ordenamos.
import numpy as np
import pandas as pd
Estamos ordenando la tabla por el parámetro area para entender dónde los NaNs juegan un papel. Estamos ordenando la tabla usando sort_values.
data = pd.read_csv('../../data/Results.csv', index_col=0, delimiter=';')
data.sort_values(by = "Area", ascending=False)
| Area | Mean | StdDev | Min | Max | X | Y | XM | YM | Major | Minor | Angle | %Area | Type | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 190 | 2755.0 | 859.928 | 235.458 | 539.0 | 3880.0 | 108.710 | 302.158 | 110.999 | 300.247 | 144.475 | 24.280 | 39.318 | 100 | C |
| 81 | 2295.0 | 765.239 | 96.545 | 558.0 | 1431.0 | 375.003 | 134.888 | 374.982 | 135.359 | 65.769 | 44.429 | 127.247 | 100 | B |
| 209 | 1821.0 | 847.761 | 122.074 | 600.0 | 1510.0 | 287.795 | 321.115 | 288.074 | 321.824 | 55.879 | 41.492 | 112.124 | 100 | A |
| 252 | 1528.0 | 763.777 | 83.183 | 572.0 | 1172.0 | 191.969 | 385.944 | 192.487 | 385.697 | 63.150 | 30.808 | 34.424 | 100 | B |
| 265 | 1252.0 | 793.371 | 117.139 | 579.0 | 1668.0 | 262.071 | 394.497 | 262.268 | 394.326 | 60.154 | 26.500 | 50.147 | 100 | A |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 113 | 1.0 | 587.000 | 0.000 | 587.0 | 587.0 | 399.500 | 117.500 | 399.500 | 117.500 | 1.128 | 1.128 | 0.000 | 100 | A |
| 310 | 1.0 | 866.000 | 0.000 | 866.0 | 866.0 | 343.500 | 408.500 | 343.500 | 408.500 | 1.128 | 1.128 | 0.000 | 100 | A |
| 219 | 1.0 | 763.000 | 0.000 | 763.0 | 763.0 | 411.500 | 296.500 | 411.500 | 296.500 | 1.128 | 1.128 | 0.000 | 100 | A |
| 3 | NaN | NaN | NaN | 608.0 | 964.0 | NaN | NaN | NaN | 7.665 | 7.359 | NaN | 101.121 | 100 | A |
| 5 | NaN | NaN | 69.438 | 566.0 | 792.0 | 348.500 | 7.500 | NaN | 7.508 | NaN | 3.088 | NaN | 100 | A |
391 rows × 14 columns
También podríamos usar esta función para ordenar a lo largo de un eje (filas/columnas).
Como puedes ver, hay filas en la parte inferior que contienen NaNs. Estas están en la parte inferior de la tabla porque pandas no puede ordenarlas.
Una comprobación rápida de si hay NaNs en cualquier parte de una tabla es un control de calidad importante para una buena práctica científica:
data.isnull().values.any()
True
Ahora sabemos que tenemos NaNs en nuestra tabla. También podemos obtener algunas ideas más profundas sobre en qué columnas se encuentran estos valores NaN.
data.isnull().sum()
Area 2
Mean 5
StdDev 3
Min 3
Max 3
X 2
Y 3
XM 3
YM 5
Major 8
Minor 3
Angle 1
%Area 0
Type 0
dtype: int64
Para tener una idea de si podemos procesar más esa tabla, es posible que queramos saber el porcentaje de NaNs para cada columna:
data.isnull().mean().sort_values(ascending=False) *100
Major 2.046036
Mean 1.278772
YM 1.278772
StdDev 0.767263
Min 0.767263
Max 0.767263
Y 0.767263
XM 0.767263
Minor 0.767263
Area 0.511509
X 0.511509
Angle 0.255754
%Area 0.000000
Type 0.000000
dtype: float64
Eliminación de filas que contienen NaNs#
Dependiendo del tipo de análisis de datos que se deba realizar, puede tener sentido simplemente ignorar las columnas que contienen valores NaN. Alternativamente, es posible eliminar las filas que contienen NaNs.
Depende de tu proyecto y de lo que sea importante o no para el análisis. No es una respuesta fácil.
data_no_nan = data.dropna(how="any")
data_no_nan
| Area | Mean | StdDev | Min | Max | X | Y | XM | YM | Major | Minor | Angle | %Area | Type | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 18.0 | 730.389 | 103.354 | 592.0 | 948.0 | 435.000 | 4.722 | 434.962 | 4.697 | 5.987 | 3.828 | 168.425 | 100 | A |
| 2 | 126.0 | 718.333 | 90.367 | 556.0 | 1046.0 | 388.087 | 8.683 | 388.183 | 8.687 | 16.559 | 9.688 | 175.471 | 100 | A |
| 4 | 68.0 | 686.985 | 61.169 | 571.0 | 880.0 | 126.147 | 8.809 | 126.192 | 8.811 | 15.136 | 5.720 | 168.133 | 100 | A |
| 6 | 669.0 | 697.164 | 72.863 | 539.0 | 957.0 | 471.696 | 26.253 | 471.694 | 26.197 | 36.656 | 23.237 | 124.340 | 100 | A |
| 7 | 5.0 | 658.600 | 49.161 | 607.0 | 710.0 | 28.300 | 8.100 | 28.284 | 8.103 | 3.144 | 2.025 | 161.565 | 100 | A |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 383 | 94.0 | 746.617 | 85.198 | 550.0 | 1021.0 | 194.032 | 498.223 | 194.014 | 498.239 | 17.295 | 6.920 | 52.720 | 100 | B |
| 387 | 152.0 | 801.599 | 111.328 | 582.0 | 1263.0 | 348.487 | 497.632 | 348.451 | 497.675 | 17.773 | 10.889 | 11.829 | 100 | A |
| 389 | 60.0 | 758.033 | 77.309 | 601.0 | 947.0 | 259.000 | 499.300 | 258.990 | 499.289 | 9.476 | 8.062 | 90.000 | 100 | A |
| 390 | 12.0 | 714.833 | 67.294 | 551.0 | 785.0 | 240.167 | 498.167 | 240.179 | 498.148 | 4.606 | 3.317 | 168.690 | 100 | A |
| 391 | 23.0 | 695.043 | 67.356 | 611.0 | 846.0 | 49.891 | 503.022 | 49.882 | 502.979 | 6.454 | 4.537 | 73.243 | 100 | A |
374 rows × 14 columns
En la parte inferior de esa tabla, puedes ver que todavía contiene 374 de las 391 columnas originales. Si eliminas filas, debes documentar en tu futura publicación científica cuántos de cuántos conjuntos de datos fueron analizados.
Ahora también podemos verificar nuevamente si hay NaNs presentes.
data_no_nan.isnull().values.any()
False
Determinación de filas que contienen NaNs#
En algunos casos de uso, podría ser útil tener una lista de índices de filas donde hay valores NaN.
data = {
'A': [0, 1, 22, 21, 12, 23],
'B': [2, 3, np.nan, 2, 12, 22],
'C': [2, 3, 44, 2, np.nan, 52],
}
table = pd.DataFrame(data)
table
| A | B | C | |
|---|---|---|---|
| 0 | 0 | 2.0 | 2.0 |
| 1 | 1 | 3.0 | 3.0 |
| 2 | 22 | NaN | 44.0 |
| 3 | 21 | 2.0 | 2.0 |
| 4 | 12 | 12.0 | NaN |
| 5 | 23 | 22.0 | 52.0 |
np.max(table.isnull().values, axis=1)
array([False, False, True, False, True, False])
Eliminación de columnas que contienen NaNs#
Como se mencionó anteriormente, a veces también tiene sentido eliminar columnas. Por ejemplo, si una columna está llena de valores NaN. Para mostrar esto, crearemos dicha columna:
data['difficult_measurement'] = np.nan
nan_table = pd.DataFrame(data)
nan_table
| A | B | C | difficult_measurement | |
|---|---|---|---|---|
| 0 | 0 | 2.0 | 2.0 | NaN |
| 1 | 1 | 3.0 | 3.0 | NaN |
| 2 | 22 | NaN | 44.0 | NaN |
| 3 | 21 | 2.0 | 2.0 | NaN |
| 4 | 12 | 12.0 | NaN | NaN |
| 5 | 23 | 22.0 | 52.0 | NaN |
Ahora podemos eliminar la columna de esta manera:
table_dropped = nan_table.drop('difficult_measurement', axis=1)
Y tener la columna nuevamente eliminada de nuestra tabla
table_dropped
| A | B | C | |
|---|---|---|---|
| 0 | 0 | 2.0 | 2.0 |
| 1 | 1 | 3.0 | 3.0 |
| 2 | 22 | NaN | 44.0 |
| 3 | 21 | 2.0 | 2.0 |
| 4 | 12 | 12.0 | NaN |
| 5 | 23 | 22.0 | 52.0 |