
#Limpiamos la memoria
rm(list = ls())
ls()
## character(0)
# Verificar el directorio de trabajo
getwd()
## [1] "C:/Users/lenovo/Documents/GitHub/Analisis-Multivariado/SESIONES/Sesión 03"
Cargamos la data
library(readr)
data = read_delim("CHURNM.CSV", delim = ";", escape_double = FALSE,
locale = locale(encoding = "WINDOWS-1252"),trim_ws = TRUE)
La función head() muestra el contenido de la tabla de datos que acabamos que cargar
#visualizamos la data
head(data)
## # A tibble: 6 × 16
## IdCliente Genero Mayor65Age Socio Dependientes MultipleLineas Internet TVPago
## <chr> <chr> <dbl> <chr> <chr> <chr> <chr> <chr>
## 1 7590-VHVEG Femen… 0 Si No No servicio t… DSL No
## 2 5575-GNVDE Mascu… 0 No No No DSL No
## 3 3668-QPYBK Mascu… 0 No No No DSL No
## 4 7795-CFOCW Mascu… 0 No No No servicio t… DSL No
## 5 9237-HQITU Femen… 0 No No No Fibra O… No
## 6 9305-CDSKC Femen… 0 No No Si Fibra O… Si
## # ℹ 8 more variables: TerminoContrato <chr>, FactElectronica <chr>,
## # MetodoPago <chr>, CargoMensual <dbl>, ImporteTotal <dbl>, pemanencia <dbl>,
## # ServicioTel <chr>, Churn <chr>
la función str() permite conocer de forma compacta la estructura interna de la tabla de datos indicando el tipo de variables, los rangos de valores y una muestra de dichos valores donde de un vistazo se puede apreciar la presencia de valores ausentes identificados mediante las siglas NA (del inglés, not allowed o no disponible)
# revisar la estructura de los datos
str(data)
## spc_tbl_ [7,043 × 16] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
## $ IdCliente : chr [1:7043] "7590-VHVEG" "5575-GNVDE" "3668-QPYBK" "7795-CFOCW" ...
## $ Genero : chr [1:7043] "Femenino" "Masculino" "Masculino" "Masculino" ...
## $ Mayor65Age : num [1:7043] 0 0 0 0 0 0 0 0 0 0 ...
## $ Socio : chr [1:7043] "Si" "No" "No" "No" ...
## $ Dependientes : chr [1:7043] "No" "No" "No" "No" ...
## $ MultipleLineas : chr [1:7043] "No servicio tel" "No" "No" "No servicio tel" ...
## $ Internet : chr [1:7043] "DSL" "DSL" "DSL" "DSL" ...
## $ TVPago : chr [1:7043] "No" "No" "No" "No" ...
## $ TerminoContrato: chr [1:7043] "Mes a mes" "Un año" "Mes a mes" "Un año" ...
## $ FactElectronica: chr [1:7043] "Si" "No" "Si" "No" ...
## $ MetodoPago : chr [1:7043] "Cheque electronico" "Cheque fisico" "Cheque fisico" "TransfBanca (automatica)" ...
## $ CargoMensual : num [1:7043] 29.9 NA 53.9 42.3 70.7 ...
## $ ImporteTotal : num [1:7043] 29.9 1889.5 108.2 1840.8 151.7 ...
## $ pemanencia : num [1:7043] 1 34 2 45 2 8 22 10 28 62 ...
## $ ServicioTel : chr [1:7043] "No" "Si" "Si" NA ...
## $ Churn : chr [1:7043] "No" "No" "Si" "No" ...
## - attr(*, "spec")=
## .. cols(
## .. IdCliente = col_character(),
## .. Genero = col_character(),
## .. Mayor65Age = col_double(),
## .. Socio = col_character(),
## .. Dependientes = col_character(),
## .. MultipleLineas = col_character(),
## .. Internet = col_character(),
## .. TVPago = col_character(),
## .. TerminoContrato = col_character(),
## .. FactElectronica = col_character(),
## .. MetodoPago = col_character(),
## .. CargoMensual = col_double(),
## .. ImporteTotal = col_double(),
## .. pemanencia = col_double(),
## .. ServicioTel = col_character(),
## .. Churn = col_character()
## .. )
## - attr(*, "problems")=<externalptr>
La función summary() muestra un resumen general de las variables de la tabla, mostrando los valores: mínimo,máximo, media, mediana, primer y tercer cuartil para las variables numéricas, indicandoademás el número específico de valores NA presentes en cada una.
# Resumen estadística de las variables numéricos
summary(data)
## IdCliente Genero Mayor65Age Socio
## Length:7043 Length:7043 Min. :0.0000 Length:7043
## Class :character Class :character 1st Qu.:0.0000 Class :character
## Mode :character Mode :character Median :0.0000 Mode :character
## Mean :0.1621
## 3rd Qu.:0.0000
## Max. :1.0000
##
## Dependientes MultipleLineas Internet TVPago
## Length:7043 Length:7043 Length:7043 Length:7043
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
##
## TerminoContrato FactElectronica MetodoPago CargoMensual
## Length:7043 Length:7043 Length:7043 Min. : 18.25
## Class :character Class :character Class :character 1st Qu.: 35.45
## Mode :character Mode :character Mode :character Median : 70.35
## Mean : 64.74
## 3rd Qu.: 89.90
## Max. :118.75
## NA's :564
## ImporteTotal pemanencia ServicioTel Churn
## Min. : 18.85 Min. : 0.00 Length:7043 Length:7043
## 1st Qu.: 402.55 1st Qu.: 9.00 Class :character Class :character
## Median :1411.35 Median :29.00 Mode :character Mode :character
## Mean :2293.12 Mean :32.39
## 3rd Qu.:3798.38 3rd Qu.:55.00
## Max. :8684.80 Max. :72.00
## NA's :604 NA's :716
Visualización de distribuciones para la variable MetodoPago
library(ggplot2)
ggplot(data = data) +
geom_bar(mapping = aes(x = MetodoPago))
La presencia de datos ausentes, perdidos, missing values, o celdas vacías, representados habitualmente en R como NA, es una problemática habitual en muchos conjuntos datos. La mayoría de las veces se debe a fallos en la transcripción de los datos o problemas durante la recogida de datos, por ejemplo, debido a la imposibilidad para obtener cierta medida u observación. Tratar con conjuntos de datos en los que existen datos ausentes puede generar problemas a la hora de aplicar diferentes análisis estadísticos o en la generación de representaciones gráficas. A fin de evitar problemas futuros, es necesario aprender a detectar y aplicar algún tipo de tratamiento.
La función is.na().Es la función más sencilla para evaluar los valores ausentes ya que presenta un valor lógico de TRUE si existe un valor ausente n una fila (funciona en vector, lista, matrices y data frame).
# Devuelve un vector lógico
head(is.na(data))
## IdCliente Genero Mayor65Age Socio Dependientes MultipleLineas Internet
## [1,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [2,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [3,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [4,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [5,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## [6,] FALSE FALSE FALSE FALSE FALSE FALSE FALSE
## TVPago TerminoContrato FactElectronica MetodoPago CargoMensual
## [1,] FALSE FALSE FALSE FALSE FALSE
## [2,] FALSE FALSE FALSE FALSE TRUE
## [3,] FALSE FALSE FALSE FALSE FALSE
## [4,] FALSE FALSE FALSE FALSE FALSE
## [5,] FALSE FALSE FALSE FALSE FALSE
## [6,] FALSE FALSE FALSE FALSE FALSE
## ImporteTotal pemanencia ServicioTel Churn
## [1,] FALSE FALSE FALSE FALSE
## [2,] FALSE FALSE FALSE FALSE
## [3,] FALSE FALSE FALSE FALSE
## [4,] FALSE FALSE TRUE FALSE
## [5,] FALSE FALSE FALSE FALSE
## [6,] FALSE FALSE FALSE FALSE
La función any(is.na()) devuelve TRUE si la tabla presenta al menos un valor ausente, sin indicar el número de valores perdidos que presenta la tabla, ni la posición
any(is.na(data))
## [1] TRUE
la función sum(is.na()), permite determinar el número de valores ausentes
# Devuelve el número de NAs que presenta la tabla
sum(is.na(data))
## [1] 2608
mean(is.na()),muestra el porcentaje de valores perdidos que presenta la tabla con la cual estamos trabajando
# Devuelve el % de valores perdidos
mean(is.na(data))
## [1] 0.02314355
En ocasiones conviene realizar una detección de valores ausentes por columnas, en lugar de por filas, para identificar si alguna de las variables del dataset presenta un determinado nivel de datos perdidos. Para ello podemos utilizar la función colMeans(is.na()) y la función colSums(is.na()).
# Detección del número de valores perdidos en cada una de las columnas que presenta la tabla
colSums(is.na(data))
## IdCliente Genero Mayor65Age Socio Dependientes
## 0 0 0 0 0
## MultipleLineas Internet TVPago TerminoContrato FactElectronica
## 0 0 0 0 0
## MetodoPago CargoMensual ImporteTotal pemanencia ServicioTel
## 0 564 604 716 724
## Churn
## 0
# Detección del % de valores perdidos en cada una de las columnas que presenta la tabla
colMeans(is.na(data), round(2))
## IdCliente Genero Mayor65Age Socio Dependientes
## 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000
## MultipleLineas Internet TVPago TerminoContrato FactElectronica
## 0.00000000 0.00000000 0.00000000 0.00000000 0.00000000
## MetodoPago CargoMensual ImporteTotal pemanencia ServicioTel
## 0.00000000 0.08007951 0.08575891 0.10166122 0.10279710
## Churn
## 0.00000000
# Determinación del porcentaje de valores perdidos respecto del total de datos para cada columna
porcentajeMiss = function(x) {
sum(is.na(x)) / length(x)*100
}
apply(data, 2, porcentajeMiss)
## IdCliente Genero Mayor65Age Socio Dependientes
## 0.000000 0.000000 0.000000 0.000000 0.000000
## MultipleLineas Internet TVPago TerminoContrato FactElectronica
## 0.000000 0.000000 0.000000 0.000000 0.000000
## MetodoPago CargoMensual ImporteTotal pemanencia ServicioTel
## 0.000000 8.007951 8.575891 10.166122 10.279710
## Churn
## 0.000000
Determinar los valores existentes en cada columna del set de datos.
# valores que tiene la columna MetodoPago
unique(data$MetodoPago)
## [1] "Cheque electronico" "Cheque fisico"
## [3] "TransfBanca (automatica)" "TarjetaCredito (automatico)"
Determinar cuantos valores NA hay en cada columna.
apply(data, MARGIN = -1, function(x) sum(is.na(x)))
## IdCliente Genero Mayor65Age Socio Dependientes
## 0 0 0 0 0
## MultipleLineas Internet TVPago TerminoContrato FactElectronica
## 0 0 0 0 0
## MetodoPago CargoMensual ImporteTotal pemanencia ServicioTel
## 0 564 604 716 724
## Churn
## 0
(Visdat y Naniar).Estas librerías permiten visualizar en forma más expedita la proporción de datos perdidos dentro de un conjunto de datos.
library(visdat) # visualización de datos
library(naniar)
library(mice)
Para determinar el patrón de datos hacemos uso de la librería mice.
md.pattern(data,rotate.names = T)
## IdCliente Genero Mayor65Age Socio Dependientes MultipleLineas Internet
## 4787 1 1 1 1 1 1 1
## 539 1 1 1 1 1 1 1
## 532 1 1 1 1 1 1 1
## 68 1 1 1 1 1 1 1
## 442 1 1 1 1 1 1 1
## 53 1 1 1 1 1 1 1
## 49 1 1 1 1 1 1 1
## 9 1 1 1 1 1 1 1
## 414 1 1 1 1 1 1 1
## 48 1 1 1 1 1 1 1
## 47 1 1 1 1 1 1 1
## 4 1 1 1 1 1 1 1
## 41 1 1 1 1 1 1 1
## 3 1 1 1 1 1 1 1
## 7 1 1 1 1 1 1 1
## 0 0 0 0 0 0 0
## TVPago TerminoContrato FactElectronica MetodoPago Churn CargoMensual
## 4787 1 1 1 1 1 1
## 539 1 1 1 1 1 1
## 532 1 1 1 1 1 1
## 68 1 1 1 1 1 1
## 442 1 1 1 1 1 1
## 53 1 1 1 1 1 1
## 49 1 1 1 1 1 1
## 9 1 1 1 1 1 1
## 414 1 1 1 1 1 0
## 48 1 1 1 1 1 0
## 47 1 1 1 1 1 0
## 4 1 1 1 1 1 0
## 41 1 1 1 1 1 0
## 3 1 1 1 1 1 0
## 7 1 1 1 1 1 0
## 0 0 0 0 0 564
## ImporteTotal pemanencia ServicioTel
## 4787 1 1 1 0
## 539 1 1 0 1
## 532 1 0 1 1
## 68 1 0 0 2
## 442 0 1 1 1
## 53 0 1 0 2
## 49 0 0 1 2
## 9 0 0 0 3
## 414 1 1 1 1
## 48 1 1 0 2
## 47 1 0 1 2
## 4 1 0 0 3
## 41 0 1 1 2
## 3 0 1 0 3
## 7 0 0 1 3
## 604 716 724 2608
Visualización gráfica de proporción de datos perdidos y donde se producen.
vis_dat(data)
Determinación del porcentaje de datos perdidos.
vis_miss(data ,sort_miss = TRUE)
cluster agrupa los datos faltantes
vis_miss(data ,sort_miss = TRUE, cluster = TRUE)
Según los resultados el porcentaje de missing, respecto al total es 2.3%
(Muy pequeño)
El porcentaje de missing de acuerdo a cada columna esta entre 1-5% (manejable)
Existen varias maneras de tratar con valores ausentes:
Rellenar los valores con la media, mediana o el valor más frecuente de la variable.
Primer caso: Eliminar todos los missing
data_sin_missing = na.omit(data)
dim(data_sin_missing)
## [1] 4787 16
Segundo Caso: Reemplazamos con la mediana y moda
library(DMwR2)
data_imp_mtc = centralImputation(data) # Mediana (numérico), moda (no numérico)
dim(data_imp_mtc)
## [1] 7043 16
vis_miss(data_imp_mtc)
Tercer Caso: Reemplazo con la media, mediana y moda
library(VIM)
data_imp_mtc2 = initialise(data, method="median") #Media (continuo), Mediana (discreto), moda (No numérico)
dim(data_imp_mtc2)
## [1] 7043 16
vis_miss(data_imp_mtc2)
Gráfico de caja (boxplot) de una variable ImporteTotal respecto al método de pago
library(plotly)
plot_ly (data = data, x = ~ImporteTotal, y = ~ MetodoPago,type = "box",
color = ~ MetodoPago)
Calculamos los valores estandarizados de cada columna utilizando la función scale()
# Estandarizar de las variables del DataFrame
data_estandar = cbind(scale(data[,12:14]),data[,1:11], data[,15:16])
head(data_estandar)
## CargoMensual ImporteTotal pemanencia IdCliente Genero Mayor65Age Socio
## 1 -1.1598031 -0.9946796 -1.27834077 7590-VHVEG Femenino 0 Si
## 2 NA -0.1773864 0.06567824 5575-GNVDE Masculino 0 No
## 3 -0.3619047 -0.9602677 -1.23761292 3668-QPYBK Masculino 0 No
## 4 -0.7458933 -0.1988115 0.51368458 7795-CFOCW Masculino 0 No
## 5 0.1982864 -0.9411500 -1.23761292 9237-HQITU Femenino 0 No
## 6 1.1607514 -0.6471987 -0.99324583 9305-CDSKC Femenino 0 No
## Dependientes MultipleLineas Internet TVPago TerminoContrato
## 1 No No servicio tel DSL No Mes a mes
## 2 No No DSL No Un año
## 3 No No DSL No Mes a mes
## 4 No No servicio tel DSL No Un año
## 5 No No Fibra Optica No Mes a mes
## 6 No Si Fibra Optica Si Mes a mes
## FactElectronica MetodoPago ServicioTel Churn
## 1 Si Cheque electronico No No
## 2 No Cheque fisico Si No
## 3 Si Cheque fisico Si Si
## 4 No TransfBanca (automatica) <NA> No
## 5 Si Cheque electronico Si Si
## 6 Si Cheque electronico Si Si
Establecemos un límite para considerar valores atípicos, por ejemplo, si el puntaje Z es mayor a 3 o menor a -3. Finalmente, utilizamos la función apply() para verificar si algún valor en cada fila del DataFrame estandarizado excede el límite establecido, y seleccionamos las filas que cumplan esta condición.
Identificando valores outliers a partir de los estandarizados - rango intercuartílico (IQR)
library(dplyr)
library(outliers)
data_imp_mtc[, 12:14]
## # A tibble: 7,043 × 3
## CargoMensual ImporteTotal pemanencia
## <dbl> <dbl> <dbl>
## 1 29.8 29.8 1
## 2 70.4 1890. 34
## 3 53.8 108. 2
## 4 42.3 1841. 45
## 5 70.7 152. 2
## 6 99.6 820. 8
## 7 89.1 1949. 22
## 8 29.8 302. 10
## 9 105. 3046. 28
## 10 70.4 3488. 62
## # ℹ 7,033 more rows
# Estandarizar los datos sin missing utilizando la función scale:
df_scaled = as.data.frame(scale(data_imp_mtc[, 12:14]))
head(df_scaled)
## CargoMensual ImporteTotal pemanencia
## 1 -1.2231168 -0.9991209 -1.33265903
## 2 0.1787729 -0.1498013 0.08401196
## 3 -0.3923673 -0.9633605 -1.28972960
## 4 -0.7921655 -0.1720659 0.55623563
## 5 0.1908880 -0.9434937 -1.28972960
## 6 1.1929795 -0.6380236 -1.03215306
# Calculamos RIQ para cada variable utilizando la función IQR y luego identificar los outliers utilizando un umbral arbitrario (por ejemplo, 1.5 veces el IQR)
outliers_IQR = df_scaled %>%
mutate_if(is.numeric, funs(outlier = ifelse(abs(.) > 1.5 * IQR(.), "Yes", "No")))
head(outliers_IQR, 10)
## CargoMensual ImporteTotal pemanencia CargoMensual_outlier
## 1 -1.2231168 -0.9991209 -1.33265903 No
## 2 0.1787729 -0.1498013 0.08401196 No
## 3 -0.3923673 -0.9633605 -1.28972960 No
## 4 -0.7921655 -0.1720659 0.55623563 No
## 5 0.1908880 -0.9434937 -1.28972960 No
## 6 1.1929795 -0.6380236 -1.03215306 No
## 7 0.8277959 -0.1224444 -0.43114113 No
## 8 -1.2265782 -0.8748731 -0.94629421 No
## 9 1.3712445 0.3784060 -0.17356458 No
## 10 0.1787729 0.5802258 1.28603583 No
## ImporteTotal_outlier pemanencia_outlier
## 1 No No
## 2 No No
## 3 No No
## 4 No No
## 5 No No
## 6 No No
## 7 No No
## 8 No No
## 9 No No
## 10 No No
Método basado en estadísticas: Outliers bivariado
# boxplot con las variables estandarizadas
boxplot(data_estandar[,1:3])
### Usando el rango intercuartil (IQR) - Cargo mensual
q1C = quantile(df_scaled$CargoMensual, 0.25)
q3C = quantile(df_scaled$CargoMensual, 0.75)
# Calcular Q1, Q3 y IQR
iqrC = q3C - q1C
# Calcular los límites para identificar los outliers
lim_infC = q1C - 1.5 * iqrC
lim_supC = q3C + 1.5 * iqrC
# Eliminar los outliers
datos_sin_outliers_Cargo = df_scaled$CargoMensual[df_scaled$CargoMensual >= lim_infC & df_scaled$ImporteTotal <= lim_supC]
# Imprimir el resultado
head(datos_sin_outliers_Cargo)
## [1] -1.2231168 0.1787729 -0.3923673 -0.7921655 0.1908880 1.1929795
#Grafica
boxplot(datos_sin_outliers_Cargo, main = "Boxplot de la variable Importe Cargo Mensual ")
q1I = quantile(df_scaled$ImporteTotal, 0.25)
q3I = quantile(df_scaled$ImporteTotal, 0.75)
# Calcular Q1, Q3 y IQR
iqrI = q3I - q1I
# Calcular los límites para identificar los outliers
lim_infI = q1I - 1.5 * iqrI
lim_supI = q3I + 1.5 * iqrI
# Eliminar los outliers
datos_sin_outliers_Importe = df_scaled$ImporteTotal[df_scaled$ImporteTotal >= lim_infI & df_scaled$ImporteTotal <= lim_supI]
# Imprimir el resultado
head(datos_sin_outliers_Importe)
## [1] -0.9991209 -0.1498013 -0.9633605 -0.1720659 -0.9434937 -0.6380236
#Grafica
boxplot(datos_sin_outliers_Importe, main = "Boxplot de la variable Importe Total") #Grafica
q1P = quantile(df_scaled$pemanencia, 0.25)
q3P = quantile(df_scaled$pemanencia, 0.75)
# Calcular Q1, Q3 y IQR
iqrP = q3P - q1P
# Calcular los límites para identificar los outliers
lim_infP = q1P - 1.5 * iqrP
lim_supP = q3P + 1.5 * iqrP
# Eliminar los outliers
datos_sin_outliers_Permanencia = df_scaled$pemanencia[df_scaled$pemanencia >= lim_infP & df_scaled$pemanencia <= lim_supP]
# Imprimir el resultado
head(datos_sin_outliers_Permanencia)
## [1] -1.33265903 0.08401196 -1.28972960 0.55623563 -1.28972960 -1.03215306
#Grafica
boxplot(datos_sin_outliers_Permanencia, main = "Boxplot de la variable Permanencia") #Grafica