5  APÉNDICE

Lectura complementaria.

5.1 La magia de los percentiles

El percentil es un concepto tan crucial en el análisis de datos que vamos a cubrirlo ampliamente en este libro. Considera cada observación con respecto a las demás. Un número aislado puede no ser significativo, pero cuando se compara con otros, aparece el concepto de distribución.

Los percentiles se utilizan en el análisis numérico, así como en la evaluación del rendimiento de un modelo predictivo.


Cómo calcular percentiles


El conjunto de datos, un consejo antes de continuar:

Esto contiene muchos indicadores sobre el desarrollo mundial. Independientemente del ejemplo del análisis numérico, la idea es proporcionar una tabla lista para usar para sociólogos, investigadores, etc. interesados en analizar este tipo de datos.

La fuente de datos original es: http://databank.worldbank.org. Ahí encontrarán un diccionario de datos que explica todas las variables.

En esta sección utilizaremos una tabla que ya está preparada para el análisis. La preparación completa de los datos paso a paso se encuentra en el capítulo Análisis numérico.

Pueden buscar el significado de cualquier indicador en data.worldbank.org. Por ejemplo, si queremos saber qué significa EN.POP.SLUM.UR.ZS, entonces escribimos: http://data.worldbank.org/indicator/EN.POP.SLUM.UR.ZS


5.1.1 Cómo calcular percentiles

Existen varios métodos para obtener el percentil. Basado en interpolaciones, la forma más fácil es ordenar la variable de forma ascendente, seleccionando el percentil que deseamos (por ejemplo, 75%), y luego observando cuál es el valor máximo si queremos elegir el 75% de la población ordenada.

Ahora vamos a usar la técnica de mantener la muestra pequeña para que podamos tener el máximo control sobre lo que está sucediendo detrás del cálculo.

Conservamos los 10 países aleatorios y visualizamos el vector de rural_poverty_headcount, que es la variable que vamos a utilizar.

data_sample = data_world_wide[
    data_world_wide['Country.Name'].isin([
        "Kazakhstan", "Zambia", "Mauritania", "Malaysia",
        "Sao Tome and Principe", "Colombia", "Haiti",
        "Fiji", "Sierra Leone", "Morocco"
    ])
].sort_values('rural_poverty_headcount')

data_sample[['Country.Name', 'rural_poverty_headcount']]
Country.Name rural_poverty_headcount
120 Malaysia 1.6
99 Kazakhstan 4.4
133 Morocco 14.4
42 Colombia 40.3
65 Fiji 44.0
125 Mauritania 59.4
164 Sao Tome and Principe 59.4
169 Sierra Leone 66.1
83 Haiti 74.9
215 Zambia 77.9

Tengan en cuenta que el vector se ordena sólo con fines didácticos. Como dijimos en el capítulo de Análisis numérico, a nuestros ojos les gusta el orden.

Ahora aplicamos la función “cuantitativa” a otra variable (el porcentaje de la población rural que vive por debajo de las líneas de pobreza):

data_sample['rural_poverty_headcount'].quantile([0, 0.25, 0.5, 0.75, 1])
0.00     1.600
0.25    20.875
0.50    51.700
0.75    64.425
1.00    77.900
Name: rural_poverty_headcount, dtype: float64

Análisis

  • Percentil 50%: el 50% de los países (cinco de ellos) tienen una rural_poverty_headcount debajo de 51.7. Podemos comprobar esto en la última tabla: estos países son: Fiji, Colombia, Marruecos, Kazajistán, y Malasia.
  • Percentil 25%: el 25% de los países están por debajo de 20.87. Aquí podemos ver una interpolación porque el 25% representa ~2.5 países. Si utilizamos este valor para filtrar los países, entonces tendremos tres países: Marruecos, Kazajistán, y Malasia.

Más información sobre los diferentes tipos de cuantiles y sus interpolaciones: consultar la documentación de numpy.quantile y pandas.Series.quantile.

5.1.1.1 Obtener las descripciones semánticas

Del último ejemplo podemos afirmar que:

  • “La mitad de los países tienen hasta un 51.7% de pobreza rural”
  • “Tres cuartas partes de los países tienen un máximo de 64.4% en cuanto a su pobreza rural” (basado en los países ordenados ascendentemente).

También podemos pensar en usar lo contrario:

  • “Una cuarta parte de los países que presentan los valores más altos de pobreza rural tienen un porcentaje de por lo menos 64.4%”

5.1.2 Calcular cuantiles personalizados

Típicamente, queremos calcular ciertos cuantiles. La variable de ejemplo será el gini_index.

¿Qué es el Índice de Gini?

Es una medida de la desigualdad de ingresos o de riqueza.

  • Un coeficiente de Gini de cero expresa igualdad perfecta donde todos los valores son iguales (por ejemplo, donde todos tienen los mismos ingresos).
  • Un coeficiente de Gini de 1 (o 100%) expresa desigualdad máxima entre los valores (por ejemplo, en un gran número de personas, donde sólo una persona tiene todos los ingresos o el consumo mientras que todas las demás no tienen ninguno, el coeficiente de Gini será muy cercano a uno).

Fuente: https://en.wikipedia.org/wiki/Gini_coefficient

Ejemplo en Python:

Si queremos obtener los cuantiles de 20, 40, 60, y 80 de la variable del índice de Gini, usamos el método quantile de pandas.

El método descarta automáticamente los NaN, pero podemos ser explícitos con dropna():

# También podemos obtener múltiples cuantiles en simultáneo
p_custom = data_world_wide['gini_index'].dropna().quantile(
    [0.2, 0.4, 0.6, 0.8])
p_custom
0.2    31.624
0.4    35.244
0.6    41.076
0.8    46.148
Name: gini_index, dtype: float64

5.1.3 Indicar dónde están la mayoría de los valores

En estadística descriptiva, queremos describir la población en términos generales. Podemos hablar de rangos usando dos percentiles. Tomemos los percentiles 10 y 90 para describir al 80% de la población.

La pobreza oscila entre el ~0.07% y el ~54% en el 80% de los países. (80% porque hicimos percentil 90 - percentil 10, centrándonos en la mitad de la población.)

Si consideramos al 80% como la mayoría de la población, entonces podríamos decir: “Normalmente (o en términos generales), la pobreza pasa de ~0.07% a ~54%”. Esta es una descripción semántica.

Observamos al 80% de la población, que parece ser un buen número para describir dónde están la mayoría de los casos. También podríamos haber usado el rango del 90% (percentil 95 - percentil 5).

5.1.3.1 ¿El percentil se relaciona con el cuartil?

Cuartil es el nombre formal para los percentiles 25, 50 y 75 (cuartos o ‘Q’). Si queremos observar el 50% de la población, debemos restar el 3er cuartil (o percentil 75) del 1er cuartil (percentil 25) para saber dónde está concentrado el 50% de los datos, también conocido como el rango intercuartil o IQR.

Percentil vs. cuantil vs. cuartil

0 cuartil = 0 cuantil = 0 percentil
1 cuartil = 0.25 cuantil = 25 percentil
2 cuartil = .5 cuantil = 50 percentil (mediana)
3 cuartil = .75 cuantil = 75 percentil
4 cuartil = 1 cuantil = 100 percentil

Créditos: (stats.stackexchange.com 2017).

5.1.4 Visualizar cuantiles

Graficar un histograma junto a los lugares donde se encuentra cada percentil puede ayudarnos a entender el concepto:

quantiles_var = data_world_wide['poverty_headcount_1.9'].dropna().quantile(
    [0.25, 0.5, 0.75])

df_p = pd.DataFrame({
    'value': quantiles_var.values,
    'quantile': ['25th', '50th', '75th']
})

fig, ax = plt.subplots(figsize=(7, 4))
sns.histplot(data_world_wide['poverty_headcount_1.9'].dropna(),
             bins=20, color='gray', alpha=0.7, ax=ax)

colors = ['#E41A1C', '#377EB8', '#4DAF4A']
for i, row in df_p.iterrows():
    ax.axvline(x=row['value'], color=colors[i],
               linestyle='--', linewidth=2, label=row['quantile'])

ax.legend(title='Quantile')
ax.set(xlabel='poverty_headcount_1.9', ylabel='Frecuencia')
sns.despine()
plt.tight_layout()
plt.show()

Visualizar cuantiles

Si sumamos todas las barras grises antes del percentil 25, tendrán aproximadamente la misma altura acumulada que la suma de las barras grises después del percentil 75.

En el último gráfico, el IQR aparece entre la primera y la última línea punteada y contiene el 50% de la población.

5.1.5 Conceptos de clasificación y top/bottom ‘X%

El concepto de clasificación es el mismo que el de las competiciones. Nos permite responder ¿cuál es el país con la tasa más alta en la variable pop_living_slums?

Usaremos el método rank de pandas con method='dense'. Asigna la posición (puesto) a cada país, pero las necesitamos en orden inverso; es decir, asignamos el rank = 1 al valor más alto.

Ahora la variable será: La población que vive en hogares marginales es la proporción de la población urbana que vive en hogares marginales. Un hogar marginal se define como un grupo de individuos que viven bajo el mismo techo y que carecen de una o más de las siguientes condiciones: acceso a agua potable, acceso a saneamiento mejorado, suficiente área habitable y durabilidad de la vivienda.

La pregunta a responder: ¿Cuáles son los seis países con las tasas más altas de personas que viven en hogares marginales?

# Crear la variable de clasificación
data_world_wide['rank_pop_living_slums'] = (
    data_world_wide['pop_living_slums']
    .rank(method='dense', ascending=False)
    .astype('Int64')
)
# Ordenar los datos según la clasificación
data_world_wide = data_world_wide.sort_values(
    'rank_pop_living_slums')

# Visualizar los primeros seis resultados
data_world_wide[['Country.Name',
                  'rank_pop_living_slums']].head(6)
Country.Name rank_pop_living_slums
177 South Sudan 1
37 Central African Republic 2
184 Sudan 3
38 Chad 4
164 Sao Tome and Principe 5
81 Guinea-Bissau 6

También podemos preguntar: ¿En qué posición está Ecuador?

data_world_wide.loc[
    data_world_wide['Country.Name'] == 'Ecuador',
    ['rank_pop_living_slums']
]
rank_pop_living_slums
57 57
5.1.5.0.1 Concepto de top/bottom ‘X%

Otra pregunta que podría interesarnos responder: ¿Cuál es el valor con el que obtengo el 10% superior de los valores más bajos?

El percentil 10 es la respuesta:

data_world_wide['pop_living_slums'].quantile(0.1)
12.5

Trabajando en lo opuesto: ¿Cuál es el valor con el que obtengo el 10% inferior de los valores más altos?

El percentil 90 es la respuesta, podemos filtrar todos los casos por encima de este valor:

data_world_wide['pop_living_slums'].quantile(0.9)
75.19999999999999

5.1.6 Uso de percentiles en el scoring de datos

Hay dos capítulos que usan este concepto:

La idea básica es desarrollar un modelo predictivo que prediga una variable binaria (yes/no). Supongamos que necesitamos puntuar nuevos casos, por ejemplo, para utilizarlos en una campaña de marketing. La pregunta a responder es:

¿Cuál es el valor del puntaje que deberíamos sugerirle a los ejecutivos de ventas para capturar el 50% de las nuevas ventas potenciales? La respuesta proviene de una combinación de análisis de percentil sobre el valor del puntaje más el análisis acumulativo de la variable objetivo actual.

Curvas de ganancia y lift (desempeño del modelo)


5.1.6.1 Estudio de caso: Distribución de la riqueza

La distribución de la riqueza es similar a la del índice de Gini y se centra en la desigualdad. Mide los activos de los propietarios (que son diferentes de los ingresos), haciendo que la comparación entre países sea más uniforme con lo que las personas pueden adquirir según el lugar en el que viven. Para una mejor definición, consulten el artículo de Wikipedia y el Informe sobre el patrimonio mundial 2013. Referencias: (Wikipedia 2017) y (Suisse 2013), respectivamente.

Citando a Wikipedia (Ref. (Wikipedia 2017)):

la mitad de la riqueza mundial pertenece al 1% de la población;

el 10% superior de los adultos posee el 85%, mientras que el 90% inferior posee el 15% restante de la riqueza total del mundo; y

el 30% de los adultos más ricos poseen el 97% de la riqueza total.

Al igual que antes, a partir de la tercera frase podemos afirmarlo: “El 3% de la riqueza total se distribuye entre el 70% de los adultos”.

“Las métricas top 10% y top 30% son los quantiles 0.9 y `0.7. La riqueza es la variable numérica.”





5.2 Quick-start funpymodeling

Este paquete contiene un conjunto de funciones relacionadas con el análisis exploratorio de datos, la preparación de datos y el desempeño del modelo. Es utilizado por personas procedentes de los negocios, la investigación y la docencia (profesores y estudiantes).

funpymodeling (en Python) están íntimamente relacionados con este libro, en el sentido de que la mayor parte de su funcionalidad se utiliza para explicar los diferentes temas abordados por el libro. Desde la versión 0.2.1, funpymodeling contiene todas las funciones migradas de R, incluyendo plotar, range01 y convert_df_to_categoric.

5.2.1 Abriendo la caja negra

Algunas funciones tienen comentarios incluidos en las líneas para que el usuario pueda abrir la caja negra y aprender cómo se desarrolló, o para afinar o mejorar cualquiera de ellas.

Todas las funciones están bien documentadas, explicando todos los parámetros con la ayuda de muchos ejemplos breves. Se puede acceder a la documentación en Python a través de: help(nombre_de_la_funcion).


5.2.1.1 Sobre este quick-start

Este quick-start se centra sólo en las funciones. Pueden consultar todas las explicaciones en torno a ellas, así como el cómo y cuándo utilizarlas, siguiendo los enlaces “Leer más aquí.” que se encuentran debajo de cada sección, que los redirigirán al libro.

A continuación está la mayoría de las funciones de funModeling/funpymodeling divididas por categoría, con su equivalente en Python.

5.2.2 Análisis exploratorio de datos

5.2.2.1 status: Estado de salud de un conjunto de datos

Caso de uso: analiza los ceros, los valores faltantes (NaN), el infinito, el tipo de datos y el número de valores únicos para un conjunto de datos determinado.

st = status(heart_disease).round(2)
print(st[['variable', 'type', 'q_nan',
          'p_nan']].to_string(index=False))
              variable    type  q_nan  p_nan
                   age float64      0   0.00
                gender float64      0   0.00
            chest_pain float64      0   0.00
resting_blood_pressure float64      0   0.00
     serum_cholestoral float64      0   0.00
   fasting_blood_sugar float64      0   0.00
       resting_electro float64      0   0.00
        max_heart_rate float64      0   0.00
           exer_angina float64      0   0.00
               oldpeak float64      0   0.00
                 slope float64      0   0.00
     num_vessels_flour float64      4   0.01
                  thal float64      2   0.01
heart_disease_severity   int64      0   0.00
     has_heart_disease  object      0   0.00

[Leer más aquí: Estado de salud de un conjunto de datos]

Nota importante sobre valores faltantes: El dataset original de Cleveland tiene 2 valores faltantes en thal y 4 en num_vessels_flour. A continuación convertiremos algunas variables categóricas a tipo str para facilitar su uso con ciertas funciones de funpymodeling. Esta conversión transforma los NaN al string literal 'nan', por lo que ya no serán detectados como valores faltantes en análisis posteriores.


5.2.2.2 plot_num: Graficar las distribuciones de variables numéricas

Solamente grafica variables numéricas.

plot_num(heart_disease)

plot_num: visualizar variables numéricas

Notas:

  • bins: configura la cantidad de segmentos (10 por defecto).
  • Para exportar el gráfico: plt.savefig("plot_num.png") antes de plt.show().

[Leer más aquí: Graficar variables numéricas]


5.2.2.3 profiling_num: Calcular varias estadísticas para variables numéricas

Obtiene varias estadísticas para variables numéricas.

df_prof = profiling_num(heart_disease)
print(df_prof[['variable', 'mean', 'std_dev', 'variation_coef']])
                 variable      mean  std_dev  variation_coef
0                     age   54.4389   9.0387          0.1660
1             exer_angina    0.3267   0.4698          1.4380
2  heart_disease_severity    0.9373   1.2285          1.3107
3          max_heart_rate  149.6073  22.8750          0.1529
4       num_vessels_flour    0.6722   0.9374          1.3945
5                 oldpeak    1.0396   1.1611          1.1169
6  resting_blood_pressure  131.6898  17.5997          0.1336
7       serum_cholestoral  246.6931  51.7769          0.2099
print(df_prof[['variable', 'p_01', 'p_05', 'p_95', 'p_99']])
                 variable    p_01   p_05   p_95    p_99
0                     age   35.00   40.0   68.0   71.00
1             exer_angina    0.00    0.0    1.0    1.00
2  heart_disease_severity    0.00    0.0    3.0    4.00
3          max_heart_rate   95.02  108.1  181.9  191.96
4       num_vessels_flour    0.00    0.0    3.0    3.00
5                 oldpeak    0.00    0.0    3.4    4.20
6  resting_blood_pressure  100.00  108.0  160.0  180.00
7       serum_cholestoral  149.00  175.1  326.9  406.74

Nota:

  • plot_num y profiling_num automáticamente excluyen variables no numéricas

[Leer más aquí: Análisis numérico en Python]


5.2.2.4 freq_tbl: Obtener las distribuciones de frecuencia de variables categóricas

# Seleccionar sólo dos variables para este ejemplo
heart_disease_2 = heart_disease[['chest_pain', 'thal']]

# Distribución de la frecuencia
freq_tbl(heart_disease_2)
  chest_pain  frequency  percentage  cumulative_perc
0        4.0        144      0.4752           0.4752
1        3.0         86      0.2838           0.7590
2        2.0         50      0.1650           0.9240
3        1.0         23      0.0759           1.0000

----------------------------------------------------------------

  thal  frequency  percentage  cumulative_perc
0  3.0        166      0.5479           0.5479
1  7.0        117      0.3861           0.9340
2  6.0         18      0.0594           0.9934
3  nan          2      0.0066           1.0000

----------------------------------------------------------------
'Variables processed: chest_pain, thal'

freq_tbl: visualizar variables categóricas

Notas:

  • freq_tbl sólo procesa variables categóricas (tipo object o category), excluyendo variables numéricas.
  • Devuelve la tabla de distribución como un DataFrame.
  • Si input está vacío, entonces se ejecuta para todas las variables categóricas.
  • na_rm indica si los valores NaN deberían ser excluidos (False por defecto).
  • Para exportar el gráfico: plt.savefig("freq.png") antes de plt.show().

[Leer más aquí: Análisis de variables categóricas]


5.2.3 Correlaciones

5.2.3.1 var_rank_info: Correlación basada en Teoría de la Información

Calcula la correlación basándose en distintas métricas de la Teoría de la Información entre todas las variables de un data frame y una variable objetivo.

# var_rank_info del paquete funpymodeling
var_rank_info(heart_disease, 'has_heart_disease')
var en mi ig gr
0 heart_disease_severity 1.8459 0.9951 0.9951 0.5391
1 thal 1.2955 0.2080 0.2080 0.1606
2 exer_angina 0.9116 0.1391 0.1391 0.1526
3 chest_pain 1.7370 0.2050 0.2050 0.1180
4 num_vessels_flour 1.5679 0.1822 0.1822 0.1162
5 slope 1.2940 0.1124 0.1124 0.0869
6 gender 0.9045 0.0573 0.0573 0.0633
7 oldpeak 2.4999 0.1557 0.1557 0.0623
8 max_heart_rate 3.3169 0.1459 0.1459 0.0440
9 age 3.2852 0.0780 0.0780 0.0237
10 resting_electro 1.0881 0.0241 0.0241 0.0222
11 resting_blood_pressure 2.9587 0.0300 0.0300 0.0101
12 serum_cholestoral 3.3210 0.0247 0.0247 0.0074
13 fasting_blood_sugar 0.6061 0.0005 0.0005 0.0008

Nota: Analiza variables numéricas y categóricas. También se utiliza con el método de discretización numérica como antes, tal como discretize_df.

[Leer más aquí: Rankear las mejores variables usando la Teoría de la Información]


5.2.3.2 cross_plot: Gráfico de distribución entre variable de entrada y variable objetivo

Obtiene la distribución relativa y absoluta entre una variable de entrada y una variable objetivo. Es útil para explicar y reportar si una variable es importante o no.

cross_plot(data=heart_disease, input=['age', 'oldpeak'],
           target='has_heart_disease')

cross_plot: Visualizar variable de entrada vs. variable objetivo

Notas:

  • auto_binning: True por defecto, muestra la variable numérica como categórica.
  • Para exportar el gráfico: plt.savefig("cross_plot.png") antes de plt.show().
  • input puede ser numérica o categórica, y target debe ser una variable binaria (dos clases).
  • Si input está vacío, entonces se ejecutará para todas las variables.

[Leer más aquí: Usando cross_plot (dataViz)]


5.2.3.3 plotar: Diagramas de caja e histogramas de densidad entre variables de entrada y objetivo

Es útil para explicar y reportar si una variable es importante o no.

Diagrama de caja:

plotar(data=heart_disease,
       input=["age", "oldpeak"],
       target="has_heart_disease",
       plot_type="boxplot")

plotar (1): visualizar un diagrama de caja

[Leer más aquí: Usando boxplots]


Histogramas de densidad:

_mtcars = pd.read_csv(
    "https://raw.githubusercontent.com/"
    "vincentarelbundock/Rdatasets/master/"
    "csv/datasets/mtcars.csv")
mtcars = _mtcars[['gear', 'cyl']].copy()
mtcars['cyl'] = mtcars['cyl'].astype(str)

plotar(data=mtcars, input="gear",
       target="cyl", plot_type="histdens")

plotar (2): visualizar histograma de densidad

[Leer más aquí: Usando histogramas de densidad]

Notes:

  • Para exportar el gráfico: plt.savefig("plotar.png") antes de plt.show().
  • Si input está vacío, entonces se ejecuta para todas las variables numéricas (saltéandose las categóricas).
  • input debe ser numérico y la variable objetivo debe ser categórica.
  • target puede ser multi-clase (no sólo binario).


5.2.3.4 categ_analysis: Análisis cuantitativo para un resultado binario

Hace un análisis numérico de una variable binaria objetivo basándose en una variable categórica de entrada, la representatividad (perc_rows) y la precisión (perc_target) de cada valor de la variable de entrada; por ejemplo, la tasa de infección por gripe por país.

df_ca = categ_analysis(data=data_country,
                       input="country",
                       target="has_flu")
n_cols = len(df_ca.columns)
mid = (n_cols + 1) // 2
print(df_ca.head(6).iloc[:, :mid].to_string(index=False))
print(df_ca.head(6).iloc[:, mid:].to_string(index=False))
       country  mean_target  sum_target
      Malaysia        1.000           1
        Mexico        0.667           2
      Portugal        0.200           1
United Kingdom        0.178           8
       Uruguay        0.175          11
        Israel        0.167           1
 perc_target  q_rows  perc_rows
       0.012       1      0.001
       0.024       3      0.003
       0.012       5      0.005
       0.096      45      0.049
       0.133      63      0.069
       0.012       6      0.007

Nota:

  • La variable input debe ser categórica.
  • La variable target debe ser binaria (dos valores posibles).

Esta función se utiliza para analizar datos cuando necesitamos reducir la cardinalidad de las variables en el modelado predictivo.

[Leer más aquí: Variables de alta cardinalidad en modelado predictivo]

5.2.4 Preparación de datos

5.2.4.1 Discretización de datos

5.2.4.1.1 discretize_get_bins + discretize_df: Convertir variables numéricas a categóricas

Necesitamos dos funciones: discretize_get_bins, que devuelve los umbrales para cada variable, y luego discretize_df, que toma el resultado de la primera función y convierte las variables deseadas. El criterio de segmentación es el de igual frecuencia.

Ejemplo: convertir sólo dos variables de un conjunto de datos.

# Paso 1: Obtener los umbrales de las variables deseadas: "max_heart_rate" y "oldpeak"
d_bins = discretize_get_bins(data=heart_disease,
                             input=["max_heart_rate", "oldpeak"],
                             n_bins=5)
# Paso 2: Aplicar el umbral para llegar al data frame final
heart_disease_discretized = discretize_df(
    data=heart_disease, data_bins=d_bins)

La siguiente imagen ilustra el resultado. Por favor noten que el nombre de la variable no cambió.

Resultados del proceso de discretización automática

Notas:

  • Este procedimiento de dos pasos está pensado para ser utilizado en producción con nuevos datos.
  • Los valores mín y máx de cada segmento serán -Inf e Inf, respectivamente.

[Leer más aquí: Discretización automática de data frames]


5.2.4.2 convert_df_to_categoric: Convertir cada columna de un data frame a variables carácter

La segmentación, o el criterio de discretización para cualquier variable numérica es igual frecuencia. Las variables factor son convertidas directamente a variables carácter.

from sklearn.datasets import load_iris
iris = load_iris(as_frame=True).frame

iris_char = convert_df_to_categoric(data=iris, n_bins=5)

# Verificar las primeras filas (en 2 partes)
print(iris_char.head().iloc[:, :3].to_string(index=False))
print(iris_char.head().iloc[:, 3:].to_string(index=False))
        sepal length (cm) sepal width (cm) petal length (cm)
               (5.0, 5.6]       (3.4, 4.4]      (0.999, 1.5]
(4.2989999999999995, 5.0]       (2.7, 3.0]      (0.999, 1.5]
(4.2989999999999995, 5.0]       (3.1, 3.4]      (0.999, 1.5]
(4.2989999999999995, 5.0]       (3.0, 3.1]      (0.999, 1.5]
(4.2989999999999995, 5.0]       (3.4, 4.4]      (0.999, 1.5]
petal width (cm)        target
    (0.099, 0.2] (-0.001, 1.0]
    (0.099, 0.2] (-0.001, 1.0]
    (0.099, 0.2] (-0.001, 1.0]
    (0.099, 0.2] (-0.001, 1.0]
    (0.099, 0.2] (-0.001, 1.0]

5.2.4.3 equal_freq: Convertir variable numérica a categórica

Convierte un vector numérico en categoría usando el criterio de igual frecuencia.

new_age = equal_freq(heart_disease['age'], n_bins=5)

# Verificar los resultados
new_age.value_counts().sort_index()
age
(28.999, 45.0]    63
(45.0, 53.0]      64
(53.0, 58.0]      71
(58.0, 62.0]      45
(62.0, 77.0]      60
Name: count, dtype: int64

[Leer más aquí: Igual frecuencia]

Notas:

  • A diferencia de discretize_get_bins, esta función no inserta -Inf y Inf como los valores mín y máx, respectivamente.


5.2.4.4 range01: Escala la variable en el rango de 0 a 1

Convierte un vector numérico a una escala que va de 0 a 1, donde 0 es el mínimo y 1 es el máximo.

oldpeak_scaled = range01(heart_disease['oldpeak'])

# Verificar los resultados
oldpeak_scaled.describe()
count    303.000000
mean       0.167678
std        0.187270
min        0.000000
25%        0.000000
50%        0.129032
75%        0.258065
max        1.000000
Name: oldpeak, dtype: float64


5.2.5 Preparación de datos con valores atípicos

5.2.5.1 hampel_outlier y tukey_outlier: Obtiene el umbral de los valores atípicos

Ambas funciones obtienen un diccionario con dos valores que indica el umbral en el que los valores son considerados atípicos. Las funciones tukey_outlier y hampel_outlier son utilizadas internamente en prep_outliers.

Usando el método de Tukey:

tukey_outlier(heart_disease['resting_blood_pressure'])
{'lower': 60.0, 'upper': 200.0}

[Leer más aquí: Método de Tukey]


Usando el método de Hampel:

hampel_outlier(heart_disease['resting_blood_pressure'])
{'lower': 85.52, 'upper': 174.48}

[Leer más aquí: Método de Hampel]


5.2.5.2 prep_outliers: Preparar los valores atípicos de un data frame

Toma un data frame y devuelve el mismo data frame más las transformaciones especificadas en el parámetro input. También funciona con un sólo vector.

Ejemplo tomando dos variables como datos de entrada:

# Obtener el umbral según el método de Hampel
hampel_outlier(heart_disease['max_heart_rate'])
{'lower': 86.28, 'upper': 219.72}
# Aplicar la función para frenar los valores atípicos en los valores del umbral
data_prep = prep_outliers(data=heart_disease,
                          input=['max_heart_rate',
                                 'resting_blood_pressure'],
                          method='hampel', type='stop')

Verificar el antes y después de la variable max_heart_rate:

print(f"Before transformation -> Min: {heart_disease['max_heart_rate'].min()}; "
      f"Max: {heart_disease['max_heart_rate'].max()}")

print(f"After transformation -> Min: {data_prep['max_heart_rate'].min()}; "
      f"Max: {data_prep['max_heart_rate'].max()}")
Before transformation -> Min: 71.0; Max: 202.0
After transformation -> Min: 86.28; Max: 202.0

Notas:

  • method puede ser: bottom_top, tukey o hampel.
  • type puede ser: stop o set_na. Si es stop todos los valores marcados como atípicos serán frenados en el umbral. Si es set_na, entonces los valores marcados serán tomados como NaN.

[Leer más aquí: Cómo lidiar con valores atípicos en Python]


5.2.6 Desempeño de un modelo predictivo

5.2.6.1 gain_lift: Curva de desempeño de ganancia y lift

Después de calcular los scores o probabilidades de la clase que queremos predecir, los pasamos a la función gain_lift, que devuelve un DataFrame con métricas de desempeño.

from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

# Crear un modelo de machine learning y obtener sus puntajes para casos positivos
df_gl = heart_disease[['age', 'oldpeak', 'has_heart_disease']].dropna().copy()
X_gl = df_gl[['age', 'oldpeak']]
y_gl = (df_gl['has_heart_disease'] == 'yes').astype(int)

# Escalar las variables
scaler = StandardScaler()
X_gl_scaled = scaler.fit_transform(X_gl)

# Ajustar modelo (equivalente a glm binomial en R)
fit_glm = LogisticRegression(random_state=42)
fit_glm.fit(X_gl_scaled, y_gl)

# Obtener los scores
df_gl['score'] = fit_glm.predict_proba(X_gl_scaled)[:, 1]

# Calcular las métricas de desempeño
gain_lift(data=df_gl, score='score', target='has_heart_disease')

ganancia y lift: visualizando el desempeño de un modelo predictivo
Population Gain Lift Score.Point
0 10.0 20.14 2.01 0.824628
1 20.0 35.25 1.76 0.693297
2 30.0 48.92 1.63 0.567138
3 40.0 61.15 1.53 0.493699
4 50.0 68.35 1.37 0.404685
5 60.0 77.70 1.30 0.340836
6 70.0 87.77 1.25 0.297857
7 80.0 92.09 1.15 0.250829
8 90.0 96.40 1.07 0.201901
9 100.0 100.00 1.00 0.122902

[Leer más aquí: Análisis de Ganancia y Lift]




stats.stackexchange.com. 2017. Percentile vs quantile vs quartile. https://stats.stackexchange.com/questions/156778/percentile-vs-quantile-vs-quartile.
Suisse, Credit. 2013. Global Wealth Report 2013. https://publications.credit-suisse.com/tasks/render/file/?fileID=BCDB1364-A105-0560-1332EC9100FF5C83.
Wikipedia. 2017. Distribution of wealth. https://en.wikipedia.org/wiki/Distribution_of_wealth.