Detección de Anomalías
En el presente documento realizaremos una inspección a la base de datos que corresponde a nuestro tema de interés, que es el agrupamiento de los consumidores de un supermercado según sus preferencias y de esta manera enviar esta información al equipo de marketing.
Comenzamos con un resumen de la base de datos que se esta usando:
library(readxl)
A <- read_excel("A.xlsx")
summary(A)## Invoice ID Branch City Customer type
## Length:1000 Length:1000 Length:1000 Length:1000
## Class :character Class :character Class :character Class :character
## Mode :character Mode :character Mode :character Mode :character
##
##
##
## Gender Product line Unit price Quantity
## Length:1000 Length:1000 Length:1000 Min. : 1.00
## Class :character Class :character Class :character 1st Qu.: 3.00
## Mode :character Mode :character Mode :character Median : 5.00
## Mean : 5.51
## 3rd Qu.: 8.00
## Max. :10.00
## Tax 5% Total Date
## Length:1000 Length:1000 Length:1000
## Class :character Class :character Class :character
## Mode :character Mode :character Mode :character
##
##
##
## Time Payment cogs
## Min. :1899-12-31 10:00:00 Length:1000 Length:1000
## 1st Qu.:1899-12-31 12:43:00 Class :character Class :character
## Median :1899-12-31 15:19:00 Mode :character Mode :character
## Mean :1899-12-31 15:24:42
## 3rd Qu.:1899-12-31 18:15:00
## Max. :1899-12-31 20:59:00
## gross margin percentage gross income Rating
## Min. :4.762e+09 Length:1000 Length:1000
## 1st Qu.:4.762e+09 Class :character Class :character
## Median :4.762e+09 Mode :character Mode :character
## Mean :4.762e+09
## 3rd Qu.:4.762e+09
## Max. :4.762e+09
Dado que el documento es muy extenso (pues cuenta con 1000 entradas con 11 características) recurriremos a funciones de R para poder analizar la base de datos.
Corrección
Como se detallo en el trabajo anterior correspondiente al Preprosesamiento de los Datos y en un Análisis de Componentes Principales (ACP) las tres componentes más representativas eran "Unit Price", "Quantity" y "Tax 5%".
Como pudimos notar al inicio, "Unit Price" y "Tax %%" son consideradas de tipo character:
is.character(A$`Unit price`)## [1] TRUE
is.character(A$`Tax 5%`)## [1] TRUE
Por lo que procedemos a transformarlas en tipo numeric. Así:
A$`Unit price` = as.numeric(A$`Unit price`)
A$`Tax 5%` = as.numeric(A$`Tax 5%`)Así, presentamos la base de datos corregida y dejando las variables relevantes para nuestro trabajo:
data <- A[,7:9]
summary(data)## Unit price Quantity Tax 5%
## Min. :10.08 Min. : 1.00 Min. : 0.5
## 1st Qu.:32.88 1st Qu.: 3.00 1st Qu.: 3785.5
## Median :55.23 Median : 5.00 Median : 13266.0
## Mean :55.67 Mean : 5.51 Mean : 41433.7
## 3rd Qu.:77.94 3rd Qu.: 8.00 3rd Qu.: 33183.2
## Max. :99.96 Max. :10.00 Max. :448785.0
Valores Faltantes
Verificaremos si existen valores faltantes en nuestra vase de datos. Para eso:
missing(data)## [1] FALSE
Como se esperaba, al ser una base de datos extraída de "kaggle.com" no existen valores faltantes en nuestra base de datos.
Visualización de valores atípicos
Para poder visualizar los posibles valores atípicos (anómalos) consideraremos la función "boxplot" para las tres variables "Unit Price", "Quantity" y "Tax 5%", así:
boxplot(data$`Unit price`)boxplot(data$Quantity)boxplot(data$`Tax 5%`)Como podemos observar en las variables "Unit Price" y "Quantity" no presentan datos anómalos visibles, al contrario de "Tax 5%", que presentan muchos valores anómalos y esto puede deberse a las políticas que rijan en el país con respecto a los impuestos. Por lo que para el estudio del presente trabajo vamos a centrarnos en la variable "Tax 5%".
Estudio de la variable
Ahora, veremos la normalidad de nuestra variable con el histograma, y mediante laa aplicación de dos pruebas estadísticas de normalidad, así
library(nortest)
library(normtest)
library(moments)
hist(data$`Tax 5%`)jb.norm.test(data$`Tax 5%`)##
## Jarque-Bera test for normality
##
## data: data$`Tax 5%`
## JB = 4322.2, p-value < 2.2e-16
lillie.test(data$`Tax 5%`)##
## Lilliefors (Kolmogorov-Smirnov) normality test
##
## data: data$`Tax 5%`
## D = 0.30857, p-value < 2.2e-16
Por lo que como el contraste de hipótesis en el test Jarque-Bera y en el test de Kolmogorov-Smirnov es:
H0:Los datos tienden una distri. Normal
H1:Los datos no tienden una distri. Normal
y como el p-valor es menor que la confiabilidaad al 95%, se tiene que la variable Taz 5% no cumplle la normalidad por lo que no tiene sentido aplicar la prueba de Grubb.
Anomalías por vecino más cercano
Primero observamos a nuestra variable Tax 5% en contraste con las demás (Unit price y Quantity)
plot( data$`Tax 5%`,data$`Unit price`)plot( data$`Tax 5%`,data$Quantity) Luego, tomemos el primer contraste (Tax 5% vs United price), obsevamos su matriz de distancias y su distancia promedio a los vecinos más cercanos, de la cual obtenemos la más grande, que será la que nos dé el valor atípico.
library(FNN)
m1 <- cbind(data$`Tax 5%`, data$`Unit price`)
knn1 <- get.knn(data =m1, k = 5)
names(knn1)## [1] "nn.index" "nn.dist"
head(knn1$nn.dist, 10)## [,1] [,2] [,3] [,4] [,5]
## [1,] 101.3717140 6310.0256735 1.022000e+04 12190.025636 12860.007340
## [2,] 1.2844454 1.5404220 2.226477e+00 3.052188 3.052937
## [3,] 270.6426603 1100.0504950 1.890000e+03 2140.022531 2880.052441
## [4,] 12.0300665 83.8360072 9.898623e+01 124.000387 191.920069
## [5,] 1540.0000629 2160.0894694 3.220000e+03 10080.014234 13770.009436
## [6,] 1060.1656416 1680.0000686 3.220000e+03 13300.009648 14280.000583
## [7,] 47.2861756 84.0004667 1.105399e+02 117.312617 175.123659
## [8,] 0.7379024 0.8720665 9.838699e-01 1.162755 2.437314
## [9,] 10.0005000 20.7487927 3.618411e+01 36.858162 52.325902
## [10,] 25.8642611 29.9675091 4.424686e+01 91.578908 92.870022
score1 <- rowMeans(knn1$nn.dist)
which.max(score1)## [1] 210
De igual manera tomamos el segundo contraste (Tax 5% vs Quantity), obsevamos su matriz de distancias y su distancia promedio a los vecinos más cercanos, de la cual obtenemos la más grande, que será la que nos dé el valor atípico.
library(FNN)
m2 <- cbind(data$`Tax 5%`, data$Quantity)
knn2 <- get.knn(data =m2, k = 5)
names(knn2)## [1] "nn.index" "nn.dist"
head(knn2$nn.dist, 10)## [,1] [,2] [,3] [,4] [,5]
## [1,] 100.019998 6310.000317 10220.00000 12190.000164 12860.000156
## [2,] 0.540000 1.131769 1.28000 1.352072 1.365613
## [3,] 270.007407 1100.001818 1890.00000 2140.000935 2880.000694
## [4,] 3.605551 83.294658 97.02062 124.000000 191.010471
## [5,] 1540.000000 2160.000926 3220.00000 10080.000198 13770.000145
## [6,] 1060.001887 1680.000000 3220.00000 13300.000150 14280.000000
## [7,] 44.045431 84.000000 107.07474 116.017240 173.046237
## [8,] 0.330000 0.390000 0.44000 0.520000 1.090000
## [9,] 10.000000 10.198039 29.15476 32.062439 37.013511
## [10,] 12.369317 22.022716 35.01428 89.005618 89.022469
score2 <- rowMeans(knn2$nn.dist)
which.max(score2)## [1] 210
En ambos casos obtenemos un valor atípico en la observación 210.
Conclusiones
Se sabe que el problema de estas observaciones atípicas requiere una especial atención, pues se ha demostrado que el efecto de los valores atípicos puede tener consecuencias perversas en cualquier análisis que se realicen posteriormente sobre los datos, estimaciones que serán sesgadas, siendo el sesgo la relación que existe entre el valor atípico y las variables explicativas incluidas en un modelo. De esta forma, la detección de los valores atípicos en los datos se presenta como indispensable, presentándose en este trabajo una metodología útil para de detección de los valores atípicos.