Descripción

Este informe presenta un análisis estadístico descriptivo de la base de clientes de una empresa. El objetivo es caracterizar el comportamiento de los clientes y la calidad de los datos para la construcción de un modelo predictivo que optimice la rentabilidad de una próxima campaña de marketing directo. Para lograrlo, se realizó un proceso riguroso de limpieza de datos, incluyendo la detección y tratamiento de duplicados, valores atípicos, e imputación de datos faltantes. La meta es diferenciar e interpretar los rasgos distintivos de los clientes que aceptaron la oferta del nuevo producto durante una campaña piloto. Los hallazgos se documentan con tablas, indicadores estadísticos (porcentajes, medidas de tendencia y dispersión) y gráficos que respaldan las interpretaciones, sirviendo como la base para una segmentación de clientes efectiva.

Objetivo general

Describir con rigor estadístico las características de los clientes y la calidad de los datos, aplicando limpieza, tratamiento de valores atípicos e imputación de faltantes, para obtener resultados confiables que sustenten la toma de decisiones.

Objetivos especificos

1) Identificar y eliminar registros duplicados, documentando el criterio utilizado y su impacto.

2) Detectar valores atípicos e inconsistencias por variable, decidir su tratamiento y comparar indicadores antes/después.

3) Cuantificar y visualizar los datos faltantes por variable, determinar el mecanismo (MCAR u otro) y aplicar imputación justificada.

Datos

El primer paso consiste en cargar la base de datos y examinar la dimensión, cantidad y nombre de las variables disponibles del dataset.

## [1] 2220   37
##  [1] "Income"               "Kidhome"              "Teenhome"            
##  [4] "Recency"              "MntWines"             "MntFruits"           
##  [7] "MntMeatProducts"      "MntFishProducts"      "MntSweetProducts"    
## [10] "MntGoldProds"         "NumDealsPurchases"    "NumWebPurchases"     
## [13] "NumCatalogPurchases"  "NumStorePurchases"    "NumWebVisitsMonth"   
## [16] "AcceptedCmp3"         "AcceptedCmp4"         "AcceptedCmp5"        
## [19] "AcceptedCmp1"         "AcceptedCmp2"         "Complain"            
## [22] "Response"             "Age"                  "Customer_Days"       
## [25] "marital_Divorced"     "marital_Married"      "marital_Single"      
## [28] "marital_Together"     "marital_Widow"        "education_2n Cycle"  
## [31] "education_Basic"      "education_Graduation" "education_Master"    
## [34] "education_PhD"        "MntTotal"             "MntRegularProds"     
## [37] "AcceptedCmpOverall"

Una técnica clave para optimizar las estrategias de marketing es la segmentación, la cúal implica dividir la base de datos de clientes en grupos homogéneos, basándose en características comunes como comportamientos de compra, atributos demográficos y otras variables relevantes. Esta estrategia permite una comprensión más profunda de las necesidades de cada perfil de cliente y, por ende, una orientación más precisa y rentable de los mensajes de marketing.

Procesamiento

El procesamiento de datos de la base de clientes comienza con una fase de preparación y exploración. Primero, se realiza una limpieza exhaustiva para asegurar la calidad de la información. Una vez limpios, los datos se someten a un análisis descriptivo inicial para obtener una visión general de la información.

Tabla 1. Descripción inicial de variables

Las 37 variables de la base de datos con su descripción
Variable_name Description Classification
Income Customer’s yearly household income Continuous quantitative
Kidhome Number of small children in customer’s household Discrete quantitative
Teenhome Number of teenagers in customer’s household Discrete quantitative
Recency Number of days since the last purchase Discrete quantitative
MntWines Amount spent on wine products in the last 2 years Continuous quantitative
MntFruits Amount spent on fruit products in the last 2 years Continuous quantitative
MntMeatProducts Amount spent on meat products in the last 2 years Continuous quantitative
MntFishProducts Amount spent on fish products in the last 2 years Continuous quantitative
MntSweetProducts Amount spent on sweet products in the last 2 years Continuous quantitative
MntGoldProds Amount spent on gold products in the last 2 years Continuous quantitative
NumDealsPurchases Number of purchases made with discount Discrete quantitative
NumWebPurchases Number of purchases made through company’s website Discrete quantitative
NumCatalogPurchases Number of purchases made using catalog Discrete quantitative
NumStorePurchases Number of purchases made directly in stores Discrete quantitative
NumWebVisitsMonth Number of visits to company’s website in the last month Discrete quantitative
AcceptedCmp3 1 if customer accepted the offer in the 3rd campaign, 0 otherwise Binary nominal qualitative
AcceptedCmp4 1 if customer accepted the offer in the 4th campaign, 0 otherwise Binary nominal qualitative
AcceptedCmp5 1 if customer accepted the offer in the 5th campaign, 0 otherwise Binary nominal qualitative
AcceptedCmp1 1 if customer accepted the offer in the 1st campaign, 0 otherwise Binary nominal qualitative
AcceptedCmp2 1 if customer accepted the offer in the 2nd campaign, 0 otherwise Binary nominal qualitative
Complain 1 if customer complained in the last 2 years Binary nominal qualitative
Response 1 if customer accepted the offer in the last campaign, 0 otherwise Binary nominal qualitative
Age Age of customer Discrete quantitative
Customer_Days Number of days since registration as a customer Discrete quantitative
marital_Divorced 1 if customer is divorced, 0 otherwise Binary nominal qualitative
marital_Married 1 if customer is married, 0 otherwise Binary nominal qualitative
marital_Single 1 if customer is single, 0 otherwise Binary nominal qualitative
marital_Together 1 if customer is in relationship, 0 otherwise Binary nominal qualitative
marital_Widow 1 if customer is a widow/widower, 0 otherwise Binary nominal qualitative
education_2n Cycle Customer has secondary education Binary nominal qualitative
education_Basic Customer has basic education Binary nominal qualitative
education_Graduation Customer has a bachelor degree Binary nominal qualitative
education_Master Customer has a masters degree Binary nominal qualitative
education_PhD Customer has a PhD Binary nominal qualitative
MntTotal Total amount spent on all the products Continuous quantitative
MntRegularProds Total amount spent on regular products Continuous quantitative
AcceptedCmpOverall Overall number of accepted campaigns Discrete quantitative

La Tabla 1 presenta las 37 variables originales del dataset, acompañadas de su descripción y tipo de clasificación.

Errores e inconsistencias

Se observa que algunas variables binarias representan categorías mutuamente excluyentes, como el estado civil o el nivel educativo. Para simplificar y mejorar la estructura del dataset, estas variables se agruparon en categorías únicas, reduciendo la dimensionalidad sin pérdida de información.

Tabla 2. Variables actualizadas

Tabla actualizada con las 29 variables de la base de datos con su descripción
Variable_name Description Classification
Income Customer’s yearly household income Continuous quantitative
Kidhome Number of small children in customer’s household Discrete quantitative
Teenhome Number of teenagers in customer’s household Discrete quantitative
Recency Number of days since the last purchase Discrete quantitative
MntWines Amount spent on wine products in the last 2 years Continuous quantitative
MntFruits Amount spent on fruit products in the last 2 years Continuous quantitative
MntMeatProducts Amount spent on meat products in the last 2 years Continuous quantitative
MntFishProducts Amount spent on fish products in the last 2 years Continuous quantitative
MntSweetProducts Amount spent on sweet products in the last 2 years Continuous quantitative
MntGoldProds Amount spent on gold products in the last 2 years Continuous quantitative
NumDealsPurchases Number of purchases made with discount Discrete quantitative
NumWebPurchases Number of purchases made through company’s website Discrete quantitative
NumCatalogPurchases Number of purchases made using catalog Discrete quantitative
NumStorePurchases Number of purchases made directly in stores Discrete quantitative
NumWebVisitsMonth Number of visits to company’s website in the last month Discrete quantitative
AcceptedCmp3 1 if customer accepted the offer in the 3rd campaign, 0 otherwise Binary nominal qualitative
AcceptedCmp4 1 if customer accepted the offer in the 4th campaign, 0 otherwise Binary nominal qualitative
AcceptedCmp5 1 if customer accepted the offer in the 5th campaign, 0 otherwise Binary nominal qualitative
AcceptedCmp1 1 if customer accepted the offer in the 1st campaign, 0 otherwise Binary nominal qualitative
AcceptedCmp2 1 if customer accepted the offer in the 2nd campaign, 0 otherwise Binary nominal qualitative
Complain 1 if customer complained in the last 2 years Binary nominal qualitative
Response 1 if customer accepted the offer in the last campaign, 0 otherwise Binary nominal qualitative
Age Age of customer Discrete quantitative
Customer_Days Number of days since registration as a customer Discrete quantitative
MntTotal Total amount spent on all the products Continuous quantitative
MntRegularProds Total amount spent on regular products Continuous quantitative
AcceptedCmpOverall Overall number of accepted campaigns Discrete quantitative
Education Customer’s education level (e.g., Basic, Graduation, Master, PhD, Secondary) Ordinal qualitative
Marital_Status Customer’s marital status (e.g., Single, Married, Divorced, Widow, Together) Nominal qualitative

La Tabla 2 presenta las 29 variables actualizadas del dataset, acompañadas de su descripción y tipo de clasificación.

Luego de la agrupación, la base quedó conformada por 29 variables. En esta versión depurada, el estado civil y el nivel educativo se representan como variables categóricas únicas, lo que facilita el análisis e interpretación.

Ademas se identificaron y eliminaron registros duplicados con el fin de evitar sesgos en el análisis. La Tabla 3 muestra el conteo de registros repetidos encontrados antes de su depuración.

Tabla 3. Tabla con el conteo de registros duplicados

Tabla con el conteo de registros duplicados
Income Kidhome Teenhome Recency MntWines MntFruits MntMeatProducts MntFishProducts MntSweetProducts MntGoldProds NumDealsPurchases NumWebPurchases NumCatalogPurchases NumStorePurchases NumWebVisitsMonth AcceptedCmp3 AcceptedCmp4 AcceptedCmp5 AcceptedCmp1 AcceptedCmp2 Complain Response Age Customer_Days MntTotal MntRegularProds AcceptedCmpOverall Education Marital_Status Frecuencia
867180 0 0 20 344 189 482 50 33 172 1 5 6 5 2 0 0 0 0 0 0 0 66 2687 1098 926 0 bachelor degree Married 4
189290 0 0 15 32 0 8 23 4 18 1 1 0 4 6 0 0 0 0 0 0 0 30 2657 67 49 0 bachelor degree Married 3
344210 1 0 81 3 3 7 6 2 9 1 1 0 2 7 0 0 0 0 0 0 0 46 2522 21 12 0 bachelor degree Married 3
399220 1 0 30 29 12 59 19 1 36 2 3 0 4 8 0 0 0 0 0 0 0 37 2659 120 84 0 bachelor degree Married 3
405900 1 1 30 154 0 50 6 11 37 5 3 4 3 6 1 0 0 0 0 0 0 51 2429 221 184 1 bachelor degree Widow/Widower 3
638410 0 1 64 635 15 100 20 7 131 1 9 3 9 6 0 0 0 0 0 0 0 52 2593 777 646 0 Masters degree Divorced 3
640140 2 1 56 406 0 30 0 0 8 7 8 2 5 7 0 0 0 1 0 0 0 74 2178 436 428 1 PhD In Relationship 3
674450 0 1 63 757 80 217 29 80 11 5 9 6 12 6 0 0 0 0 0 0 0 46 2845 1163 1152 0 bachelor degree Married 3
838440 0 0 57 901 31 345 75 31 191 1 4 4 11 1 0 0 1 0 0 0 0 68 2572 1383 1192 1 bachelor degree In Relationship 3
109790 0 0 34 8 4 10 2 2 4 2 3 0 3 5 0 0 0 0 0 0 0 31 2197 26 22 0 Masters degree Divorced 2
152870 1 0 60 1 2 8 4 3 13 2 1 1 2 7 1 0 0 0 0 0 1 41 2786 18 5 1 bachelor degree Divorced 2
160050 1 0 69 1 3 2 20 30 47 3 2 1 2 8 0 0 0 0 0 0 0 40 2843 56 9 0 Basic education Married 2
171440 1 1 96 18 2 19 0 2 6 5 3 0 4 7 0 0 0 0 0 0 0 60 2293 41 35 0 Masters degree In Relationship 2
186900 0 0 77 6 1 7 23 4 19 1 1 1 2 8 0 0 0 0 0 0 0 61 2707 41 22 0 bachelor degree Married 2
187010 1 1 95 12 4 2 10 6 10 4 2 0 4 5 0 0 0 0 0 0 0 56 2549 34 24 0 bachelor degree Single 2
199860 1 0 74 3 6 5 0 2 6 1 0 0 3 7 0 0 0 0 0 0 0 35 2386 16 10 0 bachelor degree Married 2
201300 0 0 99 0 6 3 7 6 12 1 1 0 3 8 0 0 0 0 0 0 0 46 2263 22 10 0 Secondary education Married 2
201800 0 0 27 18 42 24 15 20 18 1 2 1 4 7 0 0 0 0 0 0 0 44 2653 119 101 0 bachelor degree Single 2
204250 1 0 5 4 12 5 3 16 17 2 2 0 3 7 0 0 0 0 0 0 0 34 2767 40 23 0 Basic education Married 2
205870 1 0 39 2 3 6 4 1 9 1 1 1 2 7 0 0 0 0 0 0 0 50 2208 16 7 0 bachelor degree Single 2
219940 0 1 4 9 0 6 3 1 3 1 0 0 3 5 0 0 0 0 0 0 0 63 2711 19 16 0 bachelor degree In Relationship 2
224190 0 0 74 30 3 47 19 21 42 1 3 2 2 8 0 0 0 0 0 0 0 57 2596 120 78 0 bachelor degree Married 2
225740 2 1 28 25 0 8 2 0 2 2 2 0 3 7 0 0 0 0 0 0 0 53 2403 35 33 0 bachelor degree Married 2
226340 0 0 47 2 23 11 8 6 46 1 2 1 2 8 0 0 0 0 0 0 0 54 2688 50 4 0 Basic education In Relationship 2
228040 1 0 75 14 1 7 2 1 1 1 2 0 2 9 0 0 0 0 0 0 0 49 2492 25 24 0 bachelor degree Single 2
245940 1 0 94 1 3 6 10 0 9 1 1 0 3 5 0 0 0 0 0 0 0 41 2360 20 11 0 Basic education In Relationship 2
248820 1 0 52 1 4 10 29 0 36 1 1 1 2 6 1 0 0 0 0 0 0 42 2817 44 8 1 Basic education In Relationship 2
252930 1 0 51 15 0 11 0 2 9 1 1 1 2 8 0 0 0 0 0 0 0 51 2446 28 19 0 bachelor degree Married 2
255090 1 0 15 40 3 30 10 7 11 3 3 0 3 9 0 0 0 0 0 0 1 46 2808 90 79 0 PhD Divorced 2
260910 1 1 84 15 10 19 8 17 20 3 2 1 3 5 0 0 0 0 0 0 0 63 2283 69 49 0 bachelor degree In Relationship 2
268160 0 0 50 5 1 6 3 4 3 1 0 0 3 4 0 0 0 0 0 0 0 34 2840 19 16 0 bachelor degree Single 2
269070 1 1 10 9 1 7 0 3 2 2 1 0 3 7 0 0 0 0 0 0 0 44 2472 20 18 0 Secondary education In Relationship 2
271000 1 0 64 12 0 13 2 0 10 1 1 0 3 7 0 0 0 0 0 0 0 37 2609 27 17 0 Masters degree Married 2
282490 0 0 80 1 9 7 2 14 10 1 2 0 3 6 0 0 0 0 0 0 0 59 2173 33 23 0 Basic education Married 2
286910 1 0 56 5 4 13 8 0 4 1 1 0 3 8 0 0 0 0 0 0 0 31 2519 30 26 0 bachelor degree Married 2
296720 1 1 6 9 1 3 0 4 8 1 0 0 3 6 0 0 0 0 0 0 0 55 2633 17 9 0 bachelor degree In Relationship 2
297600 1 0 87 64 4 68 7 5 17 4 3 1 4 8 0 0 0 0 0 0 0 35 2828 148 131 0 bachelor degree Single 2

Nota: Durante la fase de exploración inicial de los datos, se identificaron valores negativos en la variable MntRegularProds, lo cual es lógicamente inconsistente. Estos errores se detectaron al generar un resumen estadístico del conjunto de datos.

##      Income           Kidhome          Teenhome         Recency     
##  Min.   :  17300   Min.   :0.0000   Min.   :0.0000   Min.   : 0.00  
##  1st Qu.: 352460   1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.:24.00  
##  Median : 514120   Median :0.0000   Median :0.0000   Median :49.00  
##  Mean   : 516809   Mean   :0.4415   Mean   :0.5076   Mean   :48.91  
##  3rd Qu.: 681480   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:74.00  
##  Max.   :1137340   Max.   :2.0000   Max.   :2.0000   Max.   :99.00  
##  NA's   :68                                                         
##     MntWines        MntFruits      MntMeatProducts MntFishProducts 
##  Min.   :   0.0   Min.   :  0.00   Min.   :   0    Min.   :  0.00  
##  1st Qu.:  24.0   1st Qu.:  2.00   1st Qu.:  16    1st Qu.:  3.00  
##  Median : 174.0   Median :  8.00   Median :  68    Median : 12.00  
##  Mean   : 301.5   Mean   : 26.34   Mean   : 166    Mean   : 37.58  
##  3rd Qu.: 499.0   3rd Qu.: 33.00   3rd Qu.: 230    3rd Qu.: 50.00  
##  Max.   :1493.0   Max.   :199.00   Max.   :1725    Max.   :259.00  
##  NA's   :20                                                        
##  MntSweetProducts  MntGoldProds    NumDealsPurchases NumWebPurchases 
##  Min.   :  0.00   Min.   :  0.00   Min.   : 0.000    Min.   : 0.000  
##  1st Qu.:  1.00   1st Qu.:  9.00   1st Qu.: 1.000    1st Qu.: 2.000  
##  Median :  8.00   Median : 24.00   Median : 2.000    Median : 4.000  
##  Mean   : 27.34   Mean   : 43.88   Mean   : 2.323    Mean   : 4.114  
##  3rd Qu.: 34.00   3rd Qu.: 56.00   3rd Qu.: 3.000    3rd Qu.: 6.000  
##  Max.   :262.00   Max.   :321.00   Max.   :15.000    Max.   :27.000  
##                                                                      
##  NumCatalogPurchases NumStorePurchases NumWebVisitsMonth  AcceptedCmp3    
##  Min.   : 0.000      Min.   : 0.000    Min.   : 0.00     Min.   :0.00000  
##  1st Qu.: 0.000      1st Qu.: 3.000    1st Qu.: 3.00     1st Qu.:0.00000  
##  Median : 2.000      Median : 5.000    Median : 6.00     Median :0.00000  
##  Mean   : 2.646      Mean   : 5.805    Mean   : 5.34     Mean   :0.07398  
##  3rd Qu.: 4.000      3rd Qu.: 8.000    3rd Qu.: 7.00     3rd Qu.:0.00000  
##  Max.   :28.000      Max.   :13.000    Max.   :20.00     Max.   :1.00000  
##                                                                           
##   AcceptedCmp4      AcceptedCmp5      AcceptedCmp1      AcceptedCmp2    
##  Min.   :0.00000   Min.   :0.00000   Min.   :0.00000   Min.   :0.00000  
##  1st Qu.:0.00000   1st Qu.:0.00000   1st Qu.:0.00000   1st Qu.:0.00000  
##  Median :0.00000   Median :0.00000   Median :0.00000   Median :0.00000  
##  Mean   :0.07741   Mean   :0.07349   Mean   :0.06565   Mean   :0.01323  
##  3rd Qu.:0.00000   3rd Qu.:0.00000   3rd Qu.:0.00000   3rd Qu.:0.00000  
##  Max.   :1.00000   Max.   :1.00000   Max.   :1.00000   Max.   :1.00000  
##                                                                         
##     Complain           Response           Age         Customer_Days 
##  Min.   :0.000000   Min.   :0.0000   Min.   : 25.00   Min.   :2159  
##  1st Qu.:0.000000   1st Qu.:0.0000   1st Qu.: 43.00   1st Qu.:2337  
##  Median :0.000000   Median :0.0000   Median : 50.00   Median :2510  
##  Mean   :0.009309   Mean   :0.1538   Mean   : 51.35   Mean   :2511  
##  3rd Qu.:0.000000   3rd Qu.:0.0000   3rd Qu.: 61.00   3rd Qu.:2688  
##  Max.   :1.000000   Max.   :1.0000   Max.   :240.00   Max.   :2858  
##                                                                     
##     MntTotal      MntRegularProds  AcceptedCmpOverall  Education        
##  Min.   :   4.0   Min.   :-283.0   Min.   :0.0000     Length:2041       
##  1st Qu.:  55.0   1st Qu.:  42.0   1st Qu.:0.0000     Class :character  
##  Median : 343.0   Median : 288.0   Median :0.0000     Mode  :character  
##  Mean   : 564.5   Mean   : 520.6   Mean   :0.3038                       
##  3rd Qu.: 965.0   3rd Qu.: 886.0   3rd Qu.:0.0000                       
##  Max.   :2491.0   Max.   :2458.0   Max.   :4.0000                       
##                                                                         
##  Marital_Status    
##  Length:2041       
##  Class :character  
##  Mode  :character  
##                    
##                    
##                    
## 
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     0.0    42.0   289.5   521.7   886.8  2458.0

Para asegurar la integridad del análisis, se procedió a la eliminación de dichos registros en la variable MntRegularProds, como una medida necesaria para garantizar la calidad y precisión del conjunto de datos, lo cual es fundamental para cualquier análisis estadístico robusto y la construcción de modelos confiables.

Análisis de datos atípicos

Para detectar valores atípicos se generaron resúmenes estadísticos (tabla4)y gráficos de caja (boxplots) de cada variable continua.
En estos gráficos, los valores atípicos aparecen resaltados en color rojo.

Tabla 4. Tabla con resumen estadísticos de variables continuas

Tabla con el estadistico de variables con outlier
Variable media mediana sd min Q1 Q3 max IQR
Income 5.175041e+05 514985.0 2.067427e+05 17300 353520.00 682425.00 1137340 328905.00
Kidhome 4.421001e-01 0.0 5.357462e-01 0 0.00 1.00 2 1.00
Teenhome 5.068695e-01 0.0 5.460636e-01 0 0.00 1.00 2 1.00
Recency 4.893768e+01 49.0 2.891136e+01 0 24.00 74.00 99 50.00
MntWines 3.018855e+02 174.5 3.329801e+02 0 24.00 501.25 1493 477.25
MntFruits 2.636997e+01 8.0 3.982598e+01 0 2.00 33.00 199 31.00
MntMeatProducts 1.662031e+02 68.0 2.198610e+02 0 16.00 230.00 1725 214.00
MntFishProducts 3.763003e+01 12.0 5.483636e+01 0 3.00 50.00 259 47.00
MntSweetProducts 2.737586e+01 8.0 4.165516e+01 0 1.00 34.00 262 33.00
MntGoldProds 4.352012e+01 24.0 5.089635e+01 0 9.00 56.00 249 47.00
NumDealsPurchases 2.326300e+00 2.0 1.887094e+00 0 1.00 3.00 15 2.00
NumWebPurchases 4.096173e+00 4.0 2.681520e+00 0 2.00 6.00 27 4.00
NumCatalogPurchases 2.649166e+00 2.0 2.797551e+00 0 0.00 4.00 28 4.00
NumStorePurchases 5.812071e+00 5.0 3.227355e+00 0 3.00 8.00 13 5.00
NumWebVisitsMonth 5.338567e+00 6.0 2.409724e+00 0 3.00 7.00 20 4.00
Age 5.136016e+01 50.0 1.305093e+01 25 43.00 61.00 240 18.00
Customer_Days 2.511545e+03 2511.0 2.026835e+02 2159 2336.25 2688.00 2858 351.75
MntTotal 5.651747e+02 344.5 5.779776e+02 4 55.00 966.50 2491 911.50
MntRegularProds 5.216546e+02 289.5 5.555954e+02 0 42.00 886.75 2458 844.75
AcceptedCmpOverall 3.042198e-01 0.0 6.834116e-01 0 0.00 0.00 4 0.00

La tabla revela que la base de datos no sigue una distribución normal y está fuertemente influenciada por valores extremos. Esta asimetría y la presencia de valores atípicos significan que un proceso de limpieza de datos es esencial antes de construir cualquier modelo. Las medidas de tendencia central como la mediana y el rango intercuartílico (IQR) son más representativas del comportamiento típico del cliente que la media y el rango total.

Graficos para mejorar el analisis de los datos atipicos

El análisis evidenció que 9 variables presentan datos atípicos. Para cuantificar este fenómeno, se construyó la Tabla 5, en la cual se detalla la cantidad de outliers identificados en cada variable.

Tabla 5. Tabla detallada de variables con conteo de outlier

Tabla de variables con los outlier
Variable Outliers Total_Outliers
Income Income NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA 68
Kidhome Kidhome 0
Teenhome Teenhome 0
Recency Recency 0
MntWines MntWines NA, NA, 1332, 1349, NA, NA, 1218, 1241, NA, 1285, 1248, 1239, 1396, 1288, 1379, NA, 1478, 1492, NA, NA, NA, NA, 1308, NA, NA, 1253, 1394, 1296, 1285, 1230, 1315, 1224, NA, NA, 1298, NA, NA, NA, NA, 1449, 1259, NA, NA, 1252, 1459, 1493, 1218, 1324, 1285, 1462, 1276, 1245, 1486, 1311 54
MntFruits MntFruits 88, 80, 100, 164, 120, 107, 111, 105, 172, 81, 106, 194, 115, 99, 90, 133, 106, 162, 189, 138, 86, 114, 153, 134, 148, 84, 162, 129, 98, 183, 129, 147, 153, 104, 80, 91, 163, 134, 152, 105, 114, 137, 190, 96, 162, 138, 83, 92, 134, 155, 151, 85, 129, 153, 129, 183, 127, 91, 107, 103, 89, 105, 181, 80, 112, 159, 81, 134, 102, 86, 123, 81, 166, 142, 133, 80, 178, 93, 107, 97, 115, 168, 91, 148, 174, 122, 123, 91, 199, 138, 101, 92, 99, 111, 80, 86, 98, 85, 107, 137, 138, 168, 93, 114, 80, 98, 172, 81, 96, 83, 117, 114, 178, 108, 130, 80, 161, 138, 124, 126, 105, 80, 80, 83, 120, 142, 104, 83, 106, 81, 185, 147, 112, 162, 86, 108, 161, 129, 93, 172, 87, 168, 151, 102, 84, 97, 127, 89, 172, 161, 104, 197, 111, 98, 194, 82, 184, 88, 99, 120, 160, 107, 108, 185, 115, 133, 140, 107, 102, 117, 131, 91, 126, 129, 147, 97, 132, 86, 120, 138, 169, 84, 92, 169, 142, 172, 114, 107, 100, 153, 86, 183, 193, 137, 108, 83, 193, 105, 149, 80, 151, 143, 80, 122, 112, 82, 90, 154, 89, 161, 159, 178, 120, 102, 132, 144, 155, 142, 80, 124, 129, 194 222
MntMeatProducts MntMeatProducts 1725, 801, 780, 925, 779, 568, 690, 812, 951, 590, 921, 756, 813, 553, 554, 740, 757, 768, 565, 573, 561, 570, 832, 785, 818, 815, 786, 591, 716, 792, 974, 594, 613, 670, 925, 761, 940, 797, 635, 592, 572, 732, 713, 653, 758, 711, 838, 559, 597, 706, 617, 742, 689, 898, 706, 597, 747, 804, 774, 929, 845, 569, 685, 873, 674, 890, 704, 606, 790, 925, 651, 708, 640, 733, 961, 568, 780, 678, 555, 842, 843, 711, 818, 864, 750, 731, 604, 915, 689, 913, 946, 672, 611, 687, 842, 622, 717, 573, 984, 694, 595, 672, 553, 573, 601, 731, 553, 687, 815, 706, 724, 827, 560, 701, 915, 639, 573, 625, 649, 575, 599, 768, 673, 850, 835, 936, 935, 853, 558, 751, 693, 629, 981, 586, 565, 697, 736, 602, 590, 899, 816, 603, 754, 835, 612, 753, 654, 567, 653, 951, 815, 553, 797, 592, 746, 932, 735, 650, 607, 968, 883, 849, 614, 818, 689, 749, 655, 845, 860, 631 170
MntFishProducts MntFishProducts 172, 225, 150, 160, 227, 156, 180, 121, 201, 189, 137, 185, 151, 123, 173, 149, 134, 173, 140, 147, 224, 254, 180, 186, 218, 133, 150, 220, 205, 162, 188, 150, 166, 216, 219, 151, 172, 212, 129, 151, 137, 156, 179, 179, 149, 197, 220, 199, 169, 240, 146, 224, 127, 136, 168, 132, 147, 231, 202, 125, 175, 179, 133, 121, 172, 164, 177, 138, 138, 175, 247, 127, 150, 227, 159, 153, 123, 134, 202, 234, 210, 168, 160, 194, 250, 138, 146, 220, 237, 250, 145, 167, 160, 150, 168, 192, 180, 137, 130, 232, 132, 125, 168, 145, 184, 134, 240, 186, 125, 219, 237, 123, 138, 158, 216, 259, 168, 219, 151, 145, 133, 137, 207, 210, 172, 182, 141, 182, 124, 171, 193, 160, 130, 242, 175, 180, 246, 188, 197, 210, 197, 219, 149, 149, 185, 142, 140, 253, 169, 258, 134, 123, 258, 179, 207, 198, 153, 168, 138, 223, 151, 188, 145, 224, 150, 216, 160, 159, 150, 181, 177, 184, 158, 129, 130, 151, 205, 173, 128, 123, 153, 145, 179, 208, 180, 208, 201, 210, 121, 212, 250, 136, 199, 229, 133, 192, 193, 224, 130, 199, 145, 202, 182, 149 204
MntSweetProducts MntSweetProducts 88, 112, 178, 167, 120, 120, 122, 105, 133, 132, 98, 103, 89, 91, 89, 113, 173, 87, 176, 102, 87, 138, 163, 124, 172, 84, 149, 92, 130, 114, 141, 147, 97, 118, 153, 189, 107, 128, 163, 133, 134, 91, 150, 137, 91, 103, 172, 95, 156, 194, 96, 191, 192, 129, 108, 107, 123, 185, 134, 121, 163, 127, 125, 198, 115, 98, 106, 101, 110, 134, 102, 160, 138, 122, 166, 136, 106, 116, 133, 120, 182, 91, 148, 161, 161, 157, 153, 123, 133, 143, 92, 169, 103, 149, 86, 146, 123, 137, 92, 91, 178, 152, 112, 115, 133, 116, 138, 92, 95, 161, 93, 160, 115, 112, 137, 172, 151, 99, 144, 151, 122, 147, 115, 195, 91, 189, 98, 175, 145, 162, 105, 96, 187, 126, 97, 108, 143, 147, 105, 160, 114, 194, 148, 85, 98, 89, 109, 151, 152, 137, 197, 125, 176, 165, 97, 121, 144, 174, 151, 112, 112, 163, 196, 107, 102, 107, 97, 88, 95, 174, 107, 118, 118, 92, 126, 107, 89, 150, 86, 169, 138, 139, 188, 142, 107, 88, 105, 94, 165, 143, 138, 95, 262, 185, 108, 84, 91, 141, 85, 179, 86, 94, 143, 100, 101, 93, 194, 129, 101, 102, 98, 151, 192, 89, 102, 192, 93, 92, 111, 133, 125, 118, 118 223
MntGoldProds MntGoldProds 176, 174, 241, 190, 169, 145, 223, 168, 172, 216, 130, 141, 172, 192, 182, 181, 152, 172, 148, 241, 146, 162, 131, 135, 203, 139, 173, 218, 142, 233, 157, 159, 172, 191, 134, 130, 153, 175, 224, 165, 190, 163, 144, 147, 207, 146, 151, 141, 154, 242, 191, 147, 174, 155, 249, 151, 183, 170, 191, 152, 227, 205, 134, 198, 139, 177, 133, 145, 141, 187, 171, 130, 205, 170, 142, 129, 175, 242, 135, 241, 174, 182, 166, 144, 172, 133, 139, 131, 195, 231, 182, 182, 158, 145, 218, 200, 187, 178, 200, 147, 149, 190, 133, 142, 168, 153, 134, 192, 132, 210, 241, 144, 210, 154, 138, 134, 176, 205, 168, 140, 246, 138, 130, 196, 144, 153, 197, 196, 133, 199, 215, 127, 134, 161, 180, 187, 195, 191, 149, 128, 183, 150, 153, 162, 232, 166, 192, 176, 183, 219, 203, 153, 133, 174, 137, 185, 224, 145, 160, 143, 204, 143, 232, 145, 149, 196, 245, 185, 154, 229, 163, 128, 153, 241, 129, 248, 129, 227, 163, 174, 152, 247, 247 183
NumDealsPurchases NumDealsPurchases 15, 7, 9, 7, 7, 7, 8, 10, 9, 7, 13, 7, 9, 9, 15, 7, 7, 7, 7, 7, 7, 7, 8, 8, 7, 7, 7, 8, 8, 7, 7, 7, 15, 11, 8, 7, 7, 12, 10, 15, 10, 8, 7, 11, 8, 7, 7, 10, 7, 7, 13, 9, 12, 7, 11, 7, 7, 9, 8, 9, 13, 8, 11, 15, 11, 8, 7, 8, 10, 8, 7, 7, 7, 8 74
NumWebPurchases NumWebPurchases 27 1
NumCatalogPurchases NumCatalogPurchases 28, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 19
NumStorePurchases NumStorePurchases 0
NumWebVisitsMonth NumWebVisitsMonth 20, 20, 14, 19, 20, 14, 19 7
Age Age 240, 240 2
Customer_Days Customer_Days 0
MntTotal MntTotal 2429, 2491, 2429 3
MntRegularProds MntRegularProds 2333, 2259, 2458, 2333 4
AcceptedCmpOverall AcceptedCmpOverall 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 3, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 3, 4, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 3, 1, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 2, 2, 1, 3, 2, 4, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 1, 1, 4, 3, 1, 3, 1, 2, 1, 2, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 1, 3, 3, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 3, 1, 2, 1, 2, 1, 3, 2, 1, 2, 1, 2, 1, 3, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 3, 2, 1, 2, 1, 1, 2, 3, 4, 1, 1, 1, 2, 2, 2, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 4, 1, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 3, 1, 1, 1, 2, 4, 1, 1, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 2, 1, 3, 1, 2, 1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 2, 2, 1, 1, 2, 1, 1, 4, 3, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 2, 2, 1, 3, 1, 1, 1, 1, 1, 3, 2, 1, 1, 1, 1, 2, 2, 4, 1, 3, 1, 1, 3, 1, 2, 1, 1, 1, 2, 1, 1, 2, 3, 1, 1, 1, 3, 1, 1, 1, 1, 2, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 3, 1, 1, 1, 2, 1, 1, 1, 1, 4, 3, 1, 3, 2, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1 431

Posteriormente, se utilizaron histogramas para observar el comportamiento de la distribución de estas variables. En dichos gráficos se incluyen los cuartiles y los límites de detección de outliers (1.5 * IQR) representados con líneas punteadas, lo que permite un análisis más detallado de los extremos de la distribución.

Tratamiento de outliers

Tras identificar la distribución y comportamiento de las variables, se aplicaron técnicas de depuración a aquellas que presentaban valores atípicos anómalos o inconsistentes. En particular:

Age y NumWebPurchases mostraron valores claramente fuera de rango, probablemente producto de errores de digitación o registros poco plausibles.

NumCatalogPurchases y NumWebVisitsMonth presentaron saltos abruptos en la secuencia de valores, lo que afectaba la homogeneidad de la distribución.

Para estas variables se implementaron dos estrategias de tratamiento de outliers:

Eliminación de outliers (Age, NumWebPurchases):

Se utilizó el método basado en el rango intercuartílico (IQR), definiendo como atípicos aquellos valores que caían por debajo de Q1 – 1.5·IQR o por encima de Q3 + 1.5·IQR.

Los registros que incumplían este criterio fueron descartados.

La comparación mediante diagramas de caja (boxplots) evidenció una reducción de la dispersión extrema, manteniendo la forma general de la distribución pero eliminando los casos irregulares.

Comparación antes y después

Las gráficas de comparación lado a lado (antes y después) para cada variable evidenciaron que:

En Age se eliminaron valores fuera de rango que distorsionaban la representación central de la población.

En NumWebPurchases desaparecieron registros atípicos que generaban colas largas en la distribución.

Recorte de outliers (NumCatalogPurchases, NumWebVisitsMonth):

Para estas variables no se eliminaron registros completos, sino que se ajustaron los valores extremos al límite superior o inferior permitido por el criterio IQR.

Esto permitió mantener la cantidad total de observaciones, evitando pérdida de información, pero suavizando el efecto de valores extremos.

Los boxplots comparativos antes y después del ajuste mostraron una mayor simetría y una reducción notable en los saltos abruptos de la distribución.

Comparación antes y después

Las gráficas de comparación lado a lado (antes y después) para cada variable evidenciaron que:

En NumCatalogPurchases y NumWebVisitsMonth, el recorte permitió conservar las tendencias originales, pero reduciendo el impacto de valores desproporcionados.

Estadísticos descriptivos actualizados

Posteriormente, se recalcularon las medidas estadísticas para las variables numéricas (exceptuando las binarias). La nueva tabla resume las métricas de tendencia central (media, mediana), dispersión (desviación estándar, IQR), y valores extremos (mínimo, máximo).

Tabla 6. Tabla actualizada con manejo de outliers

Tabla con resumen estadístico actualizado
Variable media mediana sd min Q1 Q3 max IQR
Income 5.176037e+05 515180 2.060142e+05 17300 354145 682110 1054710 327965
Kidhome 4.427518e-01 0 5.358718e-01 0 0 1 2 1
Teenhome 5.076167e-01 0 5.461188e-01 0 0 1 2 1
Recency 4.895381e+01 49 2.890509e+01 0 24 74 99 50
MntWines 3.023305e+02 176 3.330280e+02 0 24 502 1493 478
MntFruits 2.639902e+01 8 3.984694e+01 0 2 33 199 31
MntMeatProducts 1.664388e+02 68 2.199372e+02 0 16 230 1725 214
MntFishProducts 3.767617e+01 12 5.486309e+01 0 3 50 259 47
MntSweetProducts 2.727518e+01 8 4.135615e+01 0 1 34 198 33
MntGoldProds 4.355430e+01 24 5.091964e+01 0 9 56 249 47
NumDealsPurchases 2.327764e+00 2 1.887492e+00 0 1 3 15 2
NumWebPurchases 4.086978e+00 4 2.633982e+00 0 2 6 11 4
NumCatalogPurchases 2.634889e+00 2 2.720486e+00 0 0 4 10 4
NumStorePurchases 5.818182e+00 5 3.225446e+00 0 3 8 13 5
NumWebVisitsMonth 5.321867e+00 6 2.329556e+00 0 3 7 13 4
Age 5.116315e+01 50 1.163075e+01 25 43 61 80 18
Customer_Days 2.511690e+03 2511 2.025768e+02 2159 2337 2688 2858 351
MntTotal 5.658339e+02 345 5.781319e+02 4 55 968 2491 913
MntRegularProds 5.222796e+02 290 5.557461e+02 0 42 887 2458 845
AcceptedCmpOverall 3.041769e-01 0 6.836748e-01 0 0 0 4 0

Interpretación de los cambios:

La media y el máximo de las variables tratadas disminuyeron de forma significativa, lo cual confirma que los outliers inflaban artificialmente estas medidas.

La mediana y los cuartiles se mantuvieron más estables, lo que sugiere que la estructura central de los datos no se vio afectada.

El IQR se redujo, evidenciando un rango intercuartílico más representativo del comportamiento real de los clientes.

Identificación de datos faltantes

Se procedió a identificar la presencia de valores faltantes en el conjunto de datos ya depurado de outliers. Para ello, se calcularon tanto el número de observaciones ausentes por variable como el porcentaje respecto al total de registros.

El análisis gráfico mediante un barchart del porcentaje de datos faltantes por variable permitió visualizar cuáles variables eran más problemáticas, mientras que el mapa de calor (heatmap) mostró la distribución de los valores ausentes a lo largo del dataset. Estas visualizaciones confirmaron que la proporción de faltantes era baja y no se concentraba en una sola variable crítica.

Determinación del mecanismo (MCAR Test)

Para entender el mecanismo detrás de los valores faltantes, se aplicó el test MCAR de Little. Este procedimiento evalúa si los datos son Missing Completely At Random (MCAR), es decir, si las ausencias no dependen de otras variables ni de los valores en sí mismos.

Previo a la prueba, se realizó una depuración de las variables numéricas para evitar redundancia (eliminando columnas con varianza cero, de baja variabilidad y con correlación alta). De esta manera se garantizó la robustez del test.

## # A tibble: 1 × 4
##   statistic    df  p.value missing.patterns
##       <dbl> <dbl>    <dbl>            <int>
## 1      137.    46 6.43e-11                3

El test de Little reveló un valor p extremadamente bajo (p<0.001), lo que nos lleva a rechazar la hipótesis nula de que los datos faltantes son completamente aleatorios (MCAR). Este resultado sugiere que la ausencia de datos no es fortuita, sino que está potencialmente relacionada con otras variables. A pesar de esto, se optó por una imputación simple debido al bajo porcentaje de valores ausentes, lo cual no compromete la representatividad de la base de datos. Esta decisión se justifica porque, en un análisis principalmente descriptivo y exploratorio, los beneficios de una imputación más compleja no compensarían su alto costo computacional y metodológico. Para un análisis predictivo más riguroso, se recomendarían métodos de imputación avanzados que tengan en cuenta la estructura de los datos..

Imputación de datos faltantes

Dado que la proporción de datos ausentes fue de aproximadamente 4%, se optó por una estrategia de imputación simple, evitando la eliminación de registros para no reducir el tamaño de la muestra.

Para las variables numéricas, los valores faltantes se imputaron con la mediana, lo que permite reducir la influencia de valores extremos y mantener la representatividad de la tendencia central.

Para las variables categóricas, se utilizó la moda, asegurando que el valor más frecuente sustituyera las ausencias.

La verificación posterior a la imputación, a través de un gráfico de valores faltantes, confirmó que el dataset quedó completo y libre de valores NA, garantizando mayor consistencia para los análisis posteriores.

Resultados y discusión

Para identificar y comprender relaciones de fuerza entre variables numéricas, se genera una matriz de correlación. Además, es crucial para detectar multicolinealidad, un problema común en el modelado predictivo, donde variables predictoras están fuertemente correlacionadas entre sí, lo que puede inestabilizar el modelo.

Gráfico 43. Matriz de correlaciones

La matriz de correlación muestra la relación lineal entre las variables numéricas del conjunto de datos. En el gráfico de calor se observan tonalidades más intensas en algunos pares de variables, lo que indica correlaciones fuertes. Por ejemplo, el monto total de gasto (MntTotal) presenta una correlación positiva considerable con los ingresos (Income), lo cual es esperable, ya que a mayor ingreso los clientes tienden a gastar más. Otras correlaciones aparecen débiles o casi nulas, lo que evidencia independencia entre varias de las variables numéricas.

Ingresos vs Monto total (Income ~ MntTotal): El gráfico de dispersión revela una relación positiva clara. La línea de tendencia ajustada por regresión lineal confirma que a medida que aumentan los ingresos, también crece el gasto total. Sin embargo, se observa cierta dispersión en niveles altos de ingreso, lo que indica que no todos los clientes con altos ingresos necesariamente gastan proporcionalmente más.

Número de visitas web vs Ingreso (NumWebVisitsMonth ~ Income): En este caso, el gráfico no refleja una relación lineal fuerte. Aunque la recta ajustada muestra una leve tendencia, los puntos se distribuyen de manera dispersa, lo que sugiere que el número de visitas web mensuales no depende directamente del nivel de ingresos.

Edad vs Ingreso (Age ~ Income): El gráfico de densidad 2D muestra áreas con mayor concentración de observaciones. Se evidencia que los ingresos se concentran más en un rango intermedio de edad, mientras que en edades extremas (muy jóvenes o muy mayores) la dispersión es mayor. Esto indica un patrón de ingresos asociado a la edad, aunque no estrictamente lineal.

##                     Df    Sum Sq   Mean Sq F value Pr(>F)    
## factor(Education)    4 5.458e+12 1.364e+12   35.52 <2e-16 ***
## Residuals         2030 7.798e+13 3.842e+10                   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Ingresos según nivel educativo (Income ~ Education): El boxplot indica que existen diferencias en los ingresos entre los distintos niveles de educación. En particular, los niveles más altos de formación presentan medianas más elevadas en comparación con los niveles básicos. El análisis de varianza (ANOVA) confirma que estas diferencias son estadísticamente significativas, lo que sugiere que la educación influye en el nivel de ingresos de las personas.

##                     Df Sum Sq Mean Sq F value   Pr(>F)    
## factor(Education)    4    147   36.76   6.851 1.78e-05 ***
## Residuals         2030  10891    5.37                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Número de visitas web mensuales según nivel educativo (NumWebVisitsMonth ~ Education): El boxplot muestra variabilidad en el número de visitas según la educación, aunque las diferencias no son tan claras como en el caso de los ingresos. La prueba ANOVA respalda este resultado, indicando diferencias leves o menos consistentes. Esto sugiere que la educación podría estar más relacionada con los ingresos que con el comportamiento de visitas en la web.

Discusión general

Los gráficos confirman que las variables de ingreso y gasto total están fuertemente correlacionadas, un hallazgo esperado y fundamental para el modelo predictivo. La matriz de correlación visualiza esta relación, y el gráfico de dispersión de Income vs. MntTotal la detalla, mostrando una tendencia positiva que valida la premisa de que clientes con mayores ingresos tienden a gastar más. No obstante, la dispersión en los puntos de datos indica que el modelo no puede depender únicamente de esta relación; otros factores también influyen en el gasto, lo que subraya la necesidad de un enfoque multivariado.

Por otro lado, los análisis de NumWebVisitsMonth vs. Income y Age vs. Income demuestran la complejidad del comportamiento del cliente. El número de visitas a la web no tiene una relación lineal clara con los ingresos, lo que sugiere que este comportamiento de navegación está impulsado por otros factores no relacionados con la capacidad de gasto. De igual manera, aunque existe una concentración de ingresos en edades intermedias, la relación no es estrictamente lineal, lo que indica que la edad por sí sola no es un predictor perfecto de los ingresos.

Finalmente, los boxplots y las pruebas ANOVA ofrecen una visión crucial de las variables categóricas. La educación tiene un impacto estadísticamente significativo en los ingresos, con niveles de formación más altos asociados a mayores ganancias. Este es un hallazgo clave para la segmentación. Sin embargo, este mismo análisis muestra que el nivel educativo tiene una relación mucho más débil con el número de visitas a la web, lo que refuerza la idea de que la navegación en línea no está tan fuertemente ligada a factores demográficos como los ingresos.

Conclusiones

Los análisis gráficos y estadísticos revelan patrones claros que validan varias hipótesis iniciales y descartan otras, sirviendo como una base sólida para la construcción del modelo predictivo y la segmentación de clientes. 1. Validación de premisas clave: Existe una relación positiva y significativa entre el ingreso y el gasto total, lo que confirma que el ingreso es una variable predictora fundamental. 2. Identificación de variables clave para segmentación: El nivel educativo es un factor determinante en el nivel de ingresos. Este hallazgo es crucial para la segmentación, permitiendo agrupar a los clientes no solo por su capacidad de gasto, sino también por su perfil educativo, lo cual puede ser utilizado para mensajes de marketing más efectivos. 3. Comportamiento heterogéneo: variables como el número de visitas web mensuales no están directamente relacionadas con los ingresos o el nivel educativo. Esto sugiere que el comportamiento de navegación es complejo y debe ser analizado con otras variables de comportamiento para ser correctamente interpretado. 4. Necesidad de un modelo multivariado: La dispersión observada en los gráficos y las correlaciones débiles entre algunas variables refuerzan la necesidad de construir un modelo que considere múltiples factores para predecir el comportamiento del cliente de manera efectiva. Esto evitará depender de una única variable y capturará la complejidad real de la base de datos.

Referencias

Llinás Solano, H. (s.f.). Estadística matemática (teoría con R). Departamento de Matemáticas y Estadística, Universidad del Norte. Recuperado de https://rpubs.com/hllinas/MgEst_Hipotesis_teoriaR

Bruce, P., & Bruce, A. (2017). Practical Statistics for Data Scientists: 50 Essential Concepts. O’Reilly Media.

James, G., Witten, D., Hastie, T., & Tibshirani, R. (2021). An Introduction to Statistical Learning: with Applications in R. Springer.

Provost, F., & Fawcett, T. (2013). Data Science for Business: What You Need to Know about Data Mining and Data-Analytic Thinking. O’Reilly Media.

Anexos

A continuación se presenta todo el código utilizado:

#####activacion de paquetes#####
#librerias utilizadas durante el procesamiento y analisis del dataset
library(devtools)
library(dplyr)
library(ggplot2)
library(readxl)
library(tidyr)
library(naniar)
library(VIM)
library(mice)
library(caret)
library(corrplot)

###### Trabajo en base de datos######

###### Limpiezade datos######

#Cargue de base de datos
setwd('C:/Users/dolli/Documents/ESTUDIO/Maestria ciencia de datos/Primer_semestre')

datos <- read_excel("data_actividad1 (3).xlsx")
head(datos)

dim(datos) # para conocer la dimension de la base de datos
colnames(datos) # para conocer las variables que tiene la base de datos

summary(datos)

###### tabla descriptiva de las variables de la base de datos ######

variable_name <- colnames(datos)

description <- c("Customer’s yearly household income","Number of small children in customer’s household", "Number of teenagers in customer’s household","Number of days since the last purchase","Amount spent on wine products in the last 2 years",
                 "Amount spent on fruit products in the last 2 years","Amount spent on meat products in the last 2 years","Amount spent on fish products in the last 2 years","Amount spent on sweet products in the last 2 years","Amount spent on gold products in the last 2 years",
                 "Number of purchases made with discount","Number of purchases made through company’s website","Number of purchases made using catalog","Number of purchases made directly in stores",
                 "Number of visits to company’s website in the last month","1 if customer accepted the offer in the 3rd campaign, 0 otherwise","1 if customer accepted the offer in the 4th campaign, 0 otherwise","1 if customer accepted the offer in the 5th campaign, 0 otherwise",
                 "1 if customer accepted the offer in the 1st campaign, 0 otherwise","1 if customer accepted the offer in the 2nd campaign, 0 otherwise","1 if customer complained in the last 2 years","1 if customer accepted the offer in the last campaign, 0 otherwise",
                 "Age of customer","Number of days since registration as a customer","1 if customer is divorced, 0 otherwise","1 if customer is married, 0 otherwise","1 if customer is single, 0 otherwise",
                 "1 if customer is in relationship, 0 otherwise","1 if customer is a widow/widower, 0 otherwise","Customer has secondary education","Customer has basic education",
                 "Customer has a bachelor degree","Customer has a masters degree","Customer has a PhD","Total amount spent on all the products",
                 "Total amount spent on regular products","Overall number of accepted campaigns")

classification <- c("Continuous quantitative","Discrete quantitative","Discrete quantitative","Discrete quantitative","Continuous quantitative",
                    "Continuous quantitative","Continuous quantitative","Continuous quantitative","Continuous quantitative","Continuous quantitative",
                    "Discrete quantitative","Discrete quantitative","Discrete quantitative","Discrete quantitative",
                    "Discrete quantitative","Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative",
                    "Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative",
                    "Discrete quantitative","Discrete quantitative","Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative",
                    "Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative",
                    "Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative","Continuous quantitative",
                    "Continuous quantitative","Discrete quantitative")

tabla_descriptiva <- data.frame(
  Variable_name = variable_name,
  Description = description,
  Classification = classification
)
tabla_descriptiva

#imprimiendo en tabla la tabla descriptiva
knitr::kable(head(tabla_descriptiva, 37), caption = "Las 37 variables de la base de datos con su descripción")

#agrupar variables tipo categorico como educacion, estdo civil 
datos_1 <- datos %>%
  mutate(Education = case_when(
    `education_2n Cycle` == 1 ~ "Secondary education",
    education_Basic == 1 ~ "Basic education",
    education_Graduation == 1 ~ "bachelor degree",
    education_Master == 1 ~ "Masters degree",
    education_PhD == 1 ~ "PhD",
    TRUE ~ "None"
  ))

datos_1 <- datos_1 %>%
  mutate(Marital_Status = case_when(
    marital_Divorced == 1 ~ "Divorced",
    marital_Married == 1 ~ "Married",
    marital_Single == 1 ~ "Single",
    marital_Together == 1 ~ "In Relationship",
    marital_Widow == 1 ~ "Widow/Widower",
    TRUE ~ "None"
  ))

#borramos las columnas que derivaron en educacion, estado civil

datos_1 <- datos_1 %>%
  select(-`education_2n Cycle`,-education_Basic,-education_Graduation,
         -education_Master,-education_PhD,-marital_Divorced,-marital_Married,
         -marital_Single,-marital_Together,-marital_Widow)



#tabla actualizada de la descricion de variables
variable_name <- colnames(datos_1)

description <- c("Customer’s yearly household income","Number of small children in customer’s household", "Number of teenagers in customer’s household","Number of days since the last purchase","Amount spent on wine products in the last 2 years",
                 "Amount spent on fruit products in the last 2 years","Amount spent on meat products in the last 2 years","Amount spent on fish products in the last 2 years","Amount spent on sweet products in the last 2 years","Amount spent on gold products in the last 2 years",
                 "Number of purchases made with discount","Number of purchases made through company’s website","Number of purchases made using catalog","Number of purchases made directly in stores",
                 "Number of visits to company’s website in the last month","1 if customer accepted the offer in the 3rd campaign, 0 otherwise","1 if customer accepted the offer in the 4th campaign, 0 otherwise","1 if customer accepted the offer in the 5th campaign, 0 otherwise",
                 "1 if customer accepted the offer in the 1st campaign, 0 otherwise","1 if customer accepted the offer in the 2nd campaign, 0 otherwise","1 if customer complained in the last 2 years","1 if customer accepted the offer in the last campaign, 0 otherwise",
                 "Age of customer","Number of days since registration as a customer","Total amount spent on all the products",
                 "Total amount spent on regular products","Overall number of accepted campaigns","Customer’s education level (e.g., Basic, Graduation, Master, PhD, Secondary)","Customer’s marital status (e.g., Single, Married, Divorced, Widow, Together)")

classification <- c("Continuous quantitative","Discrete quantitative","Discrete quantitative","Discrete quantitative","Continuous quantitative",
                    "Continuous quantitative","Continuous quantitative","Continuous quantitative","Continuous quantitative","Continuous quantitative",
                    "Discrete quantitative","Discrete quantitative","Discrete quantitative","Discrete quantitative",
                    "Discrete quantitative","Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative",
                    "Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative","Binary nominal qualitative",
                    "Discrete quantitative","Discrete quantitative",
                    "Continuous quantitative",
                    "Continuous quantitative","Discrete quantitative","Ordinal qualitative",
                    "Nominal qualitative")

tabla_descriptiva_1 <- data.frame(
  Variable_name = variable_name,
  Description = description,
  Classification = classification
)

#imprimiendo la tabla actualizada
tabla_descriptiva_1
knitr::kable(head(tabla_descriptiva_1, 29), caption = "Tabla actualizada con las 29 variables de la base de datos con su descripción")



######identificacion de elementos duplicados ######


#me muestra cuales son los datos que estan duplicados en datalle
detalle_duplicados <- datos_1 %>%
  group_by(across(everything())) %>%
  filter(n() > 1) %>%
  ungroup()

#funcion para detectar duplicados

# Detectar y contar registros duplicados exactos
duplicados_conteo <- datos_1 %>%
  group_by(across(everything())) %>%   # Agrupa por TODAS las columnas
  summarise(Frecuencia = n(), .groups = "drop") %>%  # Cuenta repeticiones
  filter(Frecuencia > 1) %>%           # Filtra solo los que tienen duplicados
  arrange(desc(Frecuencia))            # Ordena de mayor a menor

# Mostrar resultado
knitr::kable(head(duplicados_conteo, 37), caption = "Tabla con el conteo de registros duplicados")

# Eliminar duplicados exactos y dejar solo la primera aparición
datos_sin_duplicados <- datos_1 %>%
  distinct()

#resumen estadistico de los datos para una revision general
summary(datos_sin_duplicados)

#se borra los numeros negativos observados en el resumen estadistico
datos_sin_duplicados <- datos_sin_duplicados %>% filter(MntRegularProds >= 0)

summary(datos_sin_duplicados$MntRegularProds)




###### identificacion de valores atipicos ######


# Seleccionar solo variables numéricas
numeric_vars <- datos_sin_duplicados[, sapply(datos_sin_duplicados, is.numeric)]

# selecciono solo variables cuantitativas continuas
binaria <- sapply(numeric_vars, function(x) all(x %in% c(0,1)))
var_continua <- numeric_vars[,!binaria]

# Crear tabla con estadísticos descriptivos
tabla_estadistica <- var_continua %>%
  dplyr::summarise(across(
    everything(),
    list(
      media   = ~mean(.x, na.rm = TRUE),
      mediana = ~median(.x, na.rm = TRUE),
      sd      = ~sd(.x, na.rm = TRUE),
      min     = ~min(.x, na.rm = TRUE),
      Q1      = ~quantile(.x, 0.25, na.rm = TRUE),
      Q3      = ~quantile(.x, 0.75, na.rm = TRUE),
      max     = ~max(.x, na.rm = TRUE),
      IQR     = ~IQR(.x, na.rm = TRUE)
    ),
    .names = "{.col}__{.fn}"   #  doble guion bajo para evitar conflictos
  )) %>%
  pivot_longer(
    cols = everything(),
    names_to = c("Variable", "Estadistico"),
    names_sep = "__",   #  usar doble guion bajo
    values_to = "Valor"
  ) %>%
  distinct(Variable, Estadistico, .keep_all = TRUE) %>%  #  eliminar duplicados
  pivot_wider(
    names_from = Estadistico,
    values_from = Valor
  )

knitr::kable(head(tabla_estadistica,68), caption = "Tabla con el estadistico de variables con outlier")



# Bucle para graficar cada variable en boxplot y diferenciar visualmente los outlier
for (var in names(var_continua)) {
  p <- ggplot(var_continua, aes_string(y = var)) +
    geom_boxplot(fill = "skyblue", outlier.colour = "red") +
    theme_minimal() +
    labs(title = paste("Boxplot of", var),
         y = var)
  print(p)
}


#  Función para detectar outliers con IQR 
detect_outliers <- function(x) {
  Q1 <- quantile(x, 0.25, na.rm = TRUE)
  Q3 <- quantile(x, 0.75, na.rm = TRUE)
  IQR_val <- Q3 - Q1
  lower <- Q1 - 1.5 * IQR_val
  upper <- Q3 + 1.5 * IQR_val

  outliers <- x[x < lower | x > upper]
  return(outliers)
}

#aplicamos la funcion de detectar outlier a el dataset continuo para crear la tabla outlier

tabla_outlier <- lapply(var_continua, detect_outliers)

# Convertimos a data.frame para verlo como tabla
tabla_outlier <- data.frame(
  Variable = names(tabla_outlier),
  Outliers = sapply(tabla_outlier, function(x) paste(x, collapse = ", ")),
  Total_Outliers = sapply(tabla_outlier, length)
)

#observar a detalle aquellas variables que me muestran outlier superiores al 10 %

ggplot(datos_sin_duplicados, aes(x = Income)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Income, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Income, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Income, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Income, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$Income, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Income, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$Income, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 21. Distribución de Income con cuartiles y límites de outliers"))


ggplot(datos_sin_duplicados, aes(x = MntWines)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntWines, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntWines, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntWines, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntWines, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$MntWines, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntWines, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$MntWines, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 22. Distribución de MntWines con cuartiles y límites de outliers"))


ggplot(datos_sin_duplicados, aes(x = MntFruits)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFruits, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFruits, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFruits, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFruits, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$MntFruits, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFruits, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$MntFruits, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 23. Distribución de MntFruits con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = MntMeatProducts)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntMeatProducts, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntMeatProducts, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntMeatProducts, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntMeatProducts, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$MntMeatProducts, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntMeatProducts, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$MntMeatProducts, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 24. Distribución de MntMeatProducts con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = MntFishProducts)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFishProducts, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFishProducts, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFishProducts, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFishProducts, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$MntFishProducts, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntFishProducts, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$MntFishProducts, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 25 Distribución de MntFishProducts con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = MntSweetProducts)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntSweetProducts, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntSweetProducts, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntSweetProducts, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntSweetProducts, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$MntSweetProducts, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntSweetProducts, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$MntSweetProducts, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 26. Distribución de MntSweetProducts con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = MntGoldProds)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntGoldProds, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntGoldProds, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntGoldProds, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntGoldProds, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$MntGoldProds, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntGoldProds, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$MntGoldProds, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 27. Distribución de MntGoldProds con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = NumDealsPurchases)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumDealsPurchases, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumDealsPurchases, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumDealsPurchases, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumDealsPurchases, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$NumDealsPurchases, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumDealsPurchases, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$NumDealsPurchases, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 28. Distribución de NumDealsPurchases con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = NumWebPurchases)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebPurchases, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebPurchases, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebPurchases, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebPurchases, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$NumWebPurchases, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebPurchases, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$NumWebPurchases, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 29. Distribución de NumWebPurchases con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = NumCatalogPurchases)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumCatalogPurchases, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumCatalogPurchases, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumCatalogPurchases, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumCatalogPurchases, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$NumCatalogPurchases, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumCatalogPurchases, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$NumCatalogPurchases, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 30. Distribución de NumCatalogPurchases con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = NumWebVisitsMonth)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebVisitsMonth, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebVisitsMonth, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebVisitsMonth, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebVisitsMonth, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$NumWebVisitsMonth, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$NumWebVisitsMonth, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$NumWebVisitsMonth, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 31. Distribución de NumWebVisitsMonth con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = Age)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Age, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Age, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Age, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Age, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$Age, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$Age, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$Age, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 32. Distribución de Age con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = MntTotal)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntTotal, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntTotal, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntTotal, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntTotal, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$MntTotal, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntTotal, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$MntTotal, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 33. Distribución de MntTotal con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = MntRegularProds)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntRegularProds, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntRegularProds, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntRegularProds, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntRegularProds, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$MntRegularProds, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$MntRegularProds, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$MntRegularProds, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 34. Distribución de MntRegularProds con cuartiles y límites de outliers"))

ggplot(datos_sin_duplicados, aes(x = AcceptedCmpOverall)) +
  geom_histogram(bins = 30, fill = "skyblue", color = "black", alpha = 0.7) +
  geom_vline(xintercept = quantile(datos_sin_duplicados$AcceptedCmpOverall, 0.25, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$AcceptedCmpOverall, 0.5, na.rm = TRUE),color = "blue", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$AcceptedCmpOverall, 0.75, na.rm = TRUE),color = "red", linetype = "dashed") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$AcceptedCmpOverall, 0.25, na.rm = TRUE) - 1.5 * IQR(datos_sin_duplicados$AcceptedCmpOverall, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  geom_vline(xintercept = quantile(datos_sin_duplicados$AcceptedCmpOverall, 0.75, na.rm = TRUE) + 1.5 * IQR(datos_sin_duplicados$AcceptedCmpOverall, na.rm = TRUE), color = "darkgreen", linetype = "dotted") +
  labs(title = paste("Gráfico 35. Distribución de AcceptedCmpOveralls con cuartiles y límites de outliers"))


###tratamiento de outlier #####

#Vamos a utilizar dos tecnicas dependiendo de la variable y los outlier, vamos a eliminar y vamos a recortar

#primero eliminacion de outlier variables numwebpurchases y age

# Función para eliminar outliers con IQR
eliminar_outliers <- function(x) {
  q1 <- quantile(x, 0.25, na.rm = TRUE)
  q3 <- quantile(x, 0.75, na.rm = TRUE)
  iqr <- q3 - q1
  lim_inf <- q1 - 1.5 * iqr
  lim_sup <- q3 + 1.5 * iqr
  
  x >= lim_inf & x <= lim_sup
}

#  eliminando outliers en "NumWebPurchases y "age" y recortar numcatalogopurchases y numwebvisitsmonth
datos_sin_outlier <- datos_sin_duplicados %>%
  filter(eliminar_outliers(NumWebPurchases)) %>%
  filter(eliminar_outliers(Age))
  
# Crear dos versiones de la variable age
age_original <- datos_sin_duplicados$Age
age_sin_outliers <- datos_sin_outlier$Age

# Construir dos dataframes separados
df_original <- data.frame(
  Valor = age_original,
  Estado = "Antes"
)

df_sin_outliers <- data.frame(
  Valor = age_sin_outliers,
  Estado = "Después"
)

# Unirlos
df_box <- rbind(df_original, df_sin_outliers)

# Graficar boxplots lado a lado 
ggplot(df_box, aes(x = Estado, y = Valor, fill = Estado)) +
  geom_boxplot(outlier.color = "red", alpha = 0.6) +
  labs(title = "Comparación de Outliers en 'Age'",
       x = "Age",
       y = "Valor") +
  theme_minimal()


# Crear dos versiones de la variable NumWebPurchases
numweb_original <- datos_sin_duplicados$NumWebPurchases
numweb_sin_outliers <- datos_sin_outlier$NumWebPurchases

# Construir dos dataframes separados
df_original_web <- data.frame(
  Valor = numweb_original,
  Estado = "Antes"
)

df_sin_outliers_web <- data.frame(
  Valor = numweb_sin_outliers,
  Estado = "Después"
)

# Unirlos
df_box_1 <- rbind(df_original_web, df_sin_outliers_web)

# Graficar boxplots lado a lado
ggplot(df_box_1, aes(x = Estado, y = Valor, fill = Estado)) +
  geom_boxplot(outlier.color = "red", alpha = 0.6) +
  labs(title = "Comparación de Outliers en 'NumWebPurchases'",
       x = "NumWebPurchases",
       y = "Valor") +
  theme_minimal()



#TECNICA DOS: RECORTAR

#funcion para recortar los outlier
recortar_outliers <- function(x) {
  # Q1 y Q3
  q1 <- quantile(x, 0.25, na.rm = TRUE)
  q3 <- quantile(x, 0.75, na.rm = TRUE)
  iqr <- q3 - q1
  
  # Límites
  lim_inf <- q1 - 1.5 * iqr
  lim_sup <- q3 + 1.5 * iqr
  
  # Reemplazar valores fuera de rango
  x[x < lim_inf] <- lim_inf
  x[x > lim_sup] <- lim_sup
  
  return(x)
}

datos_sin_outlier$NumWebVisitsMonth <- recortar_outliers(datos_sin_outlier$NumWebVisitsMonth)
datos_sin_outlier$NumCatalogPurchases <- recortar_outliers(datos_sin_outlier$NumCatalogPurchases)


#creando las graficas de antes y despues NumCatalogPurchases

#Crear dos versiones de la variable NumCatalogPurchases
cat_original <- datos_sin_duplicados$NumCatalogPurchases
cat_sin_outliers <- datos_sin_outlier$NumCatalogPurchases

# Construir dos dataframes separados
df_original <- data.frame(
  Valor = cat_original,
  Estado = "Antes"
)

df_sin_outliers <- data.frame(
  Valor = cat_sin_outliers,
  Estado = "Después"
)

# Unirlos
df_box_3 <- rbind(df_original, df_sin_outliers)

#Graficar boxplots lado a lado 
ggplot(df_box_3, aes(x = Estado, y = Valor, fill = Estado)) +
  geom_boxplot(outlier.color = "red", alpha = 0.6) +
  labs(title = "Comparación de Outliers en 'NumCatalogPurchases'",
       x = "Numero de catalogo",
       y = "Valor") +
  theme_minimal()


#creando las graficas de antes y despues variable NumWebVisitsMonth

# Crear dos versiones de la variable age
vist_original <- datos_sin_duplicados$NumWebVisitsMonth
vist_sin_outliers <- datos_sin_outlier$NumWebVisitsMonth

# Construir dos dataframes separados
df_original <- data.frame(
  Valor = vist_original,
  Estado = "Antes"
)

df_sin_outliers <- data.frame(
  Valor = vist_sin_outliers,
  Estado = "Después"
)

# Unirlos
df_box_4 <- rbind(df_original, df_sin_outliers)

# Graficar boxplots lado a lado 
ggplot(df_box_4, aes(x = Estado, y = Valor, fill = Estado)) +
  geom_boxplot(outlier.color = "red", alpha = 0.6) +
  labs(title = "Comparación de Outliers en 'NumVisitsMonth'",
       x = "Numero de Visitas",
       y = "Valor") +
  theme_minimal()



#tabla estadistica actualizada, con el manejo de outlier

# Función para detectar si una variable es binaria (solo 0 y 1)
es_binaria <- function(x) {
  is.numeric(x) && all(na.omit(x) %in% c(0, 1))
}

tabla_estadistica_1 <- datos_sin_outlier %>%
  summarise(across(
    where(function(col) is.numeric(col) && !es_binaria(col)),  # numéricas pero no binarias
    list(
      media   = ~mean(.x, na.rm = TRUE),
      mediana = ~median(.x, na.rm = TRUE),
      sd      = ~sd(.x, na.rm = TRUE),
      min     = ~min(.x, na.rm = TRUE),
      Q1      = ~quantile(.x, 0.25, na.rm = TRUE),
      Q3      = ~quantile(.x, 0.75, na.rm = TRUE),
      max     = ~max(.x, na.rm = TRUE),
      IQR     = ~IQR(.x, na.rm = TRUE)
    ),
    .names = "{.col}__{.fn}"
  )) %>%
  pivot_longer(
    cols = everything(),
    names_to = c("Variable", "Estadistico"),
    names_sep = "__",
    values_to = "Valor"
  ) %>%
  distinct(Variable, Estadistico, .keep_all = TRUE) %>%
  pivot_wider(
    names_from = Estadistico,
    values_from = Valor
  )

### datos faltantes ###
# identificar que variables le faltan datos en un nuevo dataframe
datos_completos = colSums(is.na(datos_sin_outlier)) %>% as.data.frame() 


#  Calcular porcentaje de datos faltantes por variable
faltantes_por_variable <- datos_sin_outlier %>%
  summarise(across(everything(), ~mean(is.na(.))*100)) %>%
  pivot_longer(cols = everything(),
               names_to = "Variable",
               values_to = "Porcentaje_Faltante")

print(faltantes_por_variable)

#  Gráfico 1: porcentaje de valores faltantes por variable
ggplot(faltantes_por_variable, aes(x = reorder(Variable, -Porcentaje_Faltante), 
                                   y = Porcentaje_Faltante)) +
  geom_col(fill = "steelblue") +
  coord_flip() +
  labs(title = "Porcentaje de datos faltantes por variable",
       x = "Variable", y = "% de datos faltantes") +
  theme_minimal()


# Gráfico 2  mapa de calor de faltantes
vis_miss(datos_sin_outlier) +
  labs(title = "Mapa de calor de valores faltantes")


### DETERMINACIÓN DEL MECANISMO (MCAR test) ###

# Seleccionar solo numéricas
datos_num <- datos_sin_outlier %>% select(where(is.numeric))

#  Filtrar columnas problemáticas
datos_num <- datos_num %>%
  select(where(~ var(., na.rm = TRUE) > 0))   # elimina varianza cero

nzv <- nearZeroVar(datos_num)                 # elimina variables near-zero variance
if (length(nzv) > 0) datos_num <- datos_num[, -nzv]

cor_mat <- cor(datos_num, use = "pairwise.complete.obs")  # elimina redundantes
altas_cor <- findCorrelation(cor_mat, cutoff = 0.95)
if (length(altas_cor) > 0) datos_num <- datos_num[, -altas_cor]

#  Aplicar Little’s MCAR test (naniar)
resultado_mcar <- mcar_test(datos_num)
print(resultado_mcar)


### IMPUTACIÓN DE DATOS FALTANTES  ###

# Decisión: como solo hay ~4% de faltantes, se usa imputación simple (mediana/moda)

# Imputar numéricas con mediana
datos_imputados <- datos_sin_outlier %>%
  mutate(across(where(is.numeric), 
                ~ ifelse(is.na(.), median(., na.rm = TRUE), .)))

#  Imputar categóricas con la moda
mode_value <- function(x) {
  ux <- na.omit(unique(x))
  ux[which.max(tabulate(match(x, ux)))]
}
datos_imputados <- datos_imputados %>%
  mutate(across(where(is.factor), 
                ~ ifelse(is.na(.), mode_value(.), .)))

#  Verificar gráfica ya no hay NA
gg_miss_var(datos_imputados) +
  labs(title = "Verificación post-imputación (sin valores faltantes)")


# data limpia
data_limpia <-data.frame(datos_imputados)


#pos analisis

print(head(data_limpia))
print(dim(data_limpia))

#agrupamos las variables numericas y categoricas por aparte para facilitar la graficacion
var_categoricas <- data_limpia %>% select_if(~ is.factor(.) | is.character(.))  # Solo variables categóricas
var_continuas   <- data_limpia %>% select_if(is.numeric)  # Solo variables numéricas continuas

  
# Matriz de correlación
  cor_matrix <- cor(var_continuas, use = "complete.obs")
  print(cor_matrix)
  
corrplot(cor_matrix, method = "color", type = "upper",
          tl.col = "black", tl.srt = 90,  tl.cex = 0.4)
  
#numeral vs numeral
ggplot(data_limpia, aes(x = Income, y = MntTotal)) +
  geom_point(color = "blue", alpha = 0.6) +
  geom_smooth(method = "lm", se = FALSE, color = "red") +
  theme_minimal() +
  labs(title = "Gráfico 44. Relación entre ingreso y monto total")

ggplot(data_limpia, aes(x = NumWebVisitsMonth, y = Income)) +
  geom_point(color = "blue", alpha = 0.6) +
  geom_smooth(method = "lm", se = FALSE, color = "red") +
  theme_minimal() +
  labs(title = "Gráfico 45. Relación entre numero de visitas al mes y ingresos")

ggplot(data_limpia, aes(x = Age, y = Income)) +
  geom_point(alpha = 0.3) +
  geom_density_2d(color = "red") +
  theme_minimal() +
  labs(title = "Gráfico 46. Densidad Age vs Income")

#categoria vs numeral

ggplot(data_limpia, aes(x = factor(Education), y = Income)) +
  geom_boxplot(fill = "lightblue") +
  theme_minimal() +
  labs(title = "Gráfico 47. Ingresos por nivel educativo", x = "Education", y = "Income")

anova_result <- aov(Income ~ factor(Education), data = data_limpia)
summary(anova_result)

ggplot(data_limpia, aes(x = factor(Education), y = NumWebVisitsMonth)) +
  geom_boxplot(fill = "lightgreen") +
  theme_minimal() +
  labs(title = "Gráfico 48. Numero de visitas al mes por educación", x = "Education", y = "NumWebVisitsMonth")

anova_result <- aov(NumWebVisitsMonth ~ factor(Education), data = data_limpia)
summary(anova_result)