• Objetivo
  • Descripción de Actividad
  • Métodos
  • Descripción Datos
    • Carga de los datos
  • Análisis de los datos
    • Valores Atípicos Outliers
    • Verificación de valores nulos
    • Valores que no corresponda con el formato de cada variable:
  • Representación Geográfica
  • Cluster K-MEANS
    • Ubicacion Antenas por Clustering Kmeans
  • MCLUST
    • Ubicacion Antenas por Clustering MCLUST
  • DBSCAN Density Based Scan
    • Ubicacion Antenas por Clustering DBSCAN
    • Referencias

Soy estudiante de MsC Data en Universitat Oberta de Catalunya, con esta serie de informes busco ofrecer un blog personal que permitan evidenciar el crecimiento académico que se puede ir adquiriendo con el paso del Master y como R Studio es una herramienta de gran uso.

En este nuevo informe, realizaremos un ánalisis descriptivo y predictivo de una fuente de datos real que ha sido modificada para el caso de estudio, representandola en un mapa por posiciones geograficas

Librerias

# Librerias
library(dplyr) # Para tratamiento de datos
library(ggplot2) # para graficos
library(leaflet) # para mapas
library(cluster)
library(mclust)
library(factoextra)
library(fpc)
library(dbscan)

Objetivo

Identificar sectores geograficos donde se caé constantemente la red móvil, proponiendo ubicaciones especificas para iniciar el levantamiento de redes operadoras de telefonía móvil en el departamento de Cundinamarca, Colombia.

Descripción de Actividad

En esta actividad se realizará un análisis sobre un grupo de clientes de una compañia de telefonía que han decidido compartir su ubicación cuando una de las llamadas se cae por falta de conexión. Se plantea como objetivo lograr identificar clusters (grupos) de clientes por posición geográfica, con el fin de identificar las zonas demayor perdida de señal, logrando que la alta gerencia de la compañia de telefonía tome decisiones que garanticen la prestación de un mejor servicio.

Métodos

Los métodos de clustering que se aplicarán en este documento son:

  • Kmeans
  • Model Base Clustering
  • Density Based Scan

Descripción Datos

El set de datos ha sido tomado del curso de Ciudadano de Datos impartido por la Alianza CAOBA (Alianza Big Data para Colombia) al cual asistí hace algún tiempo. El set cuenta con diferentes variables, entre las cuales estan compartir, lat y lon que seran las variables que utilizaremos para extraer los clientes que decidieron compartir su ubicación al momento de la caida de la llamada. Este set puede ser tratado para la aplicación de algoritmos supervisados y no supervisados.

Desrcargar Set

Carga de los datos

clientes_telefonia<- read.csv("clientes_telefonia.csv", sep = ",", na.strings = "NA", header = T, 
                 fill = T, row.names = NULL, fileEncoding="utf-8" )
names(clientes_telefonia) <- c("estado","meses","ingresos","casa",
                       "precio_dispositivo","promedio_duracion_llamada","satisfaccion","sobrecargo",
                       "saldo_restante","compartir","lon","lat")

Análisis de los datos

Para empezar, el archivo cuenta con 23162 observaciones y 12 variables (estado, meses, ingresos, casa, precio_dispositivo, promedio_duracion_llamada, satisfaccion, sobrecargo, saldo_restante, compartir, lon, lat). Los tipos de variables son:

str(clientes_telefonia)
## 'data.frame':    23162 obs. of  12 variables:
##  $ estado                   : Factor w/ 2 levels "RETIRADO","VINCULADO": 2 2 1 1 1 2 1 1 2 1 ...
##  $ meses                    : num  26 23 38 37 19 19 20 32 41 19 ...
##  $ ingresos                 : num  4074840 8574088 795993 8115015 8187499 ...
##  $ casa                     : num  8.83e+08 1.74e+09 9.50e+07 8.30e+07 6.77e+08 ...
##  $ precio_dispositivo       : num  1444153 2157661 452809 2617184 666069 ...
##  $ promedio_duracion_llamada: num  2.3 3.7 16.8 17.6 0 2.2 14.8 1.3 3 17.8 ...
##  $ satisfaccion             : num  1.76 3.36 8.36 8.96 8.81 ...
##  $ sobrecargo               : num  11.2 0 350.6 230.9 131.5 ...
##  $ saldo_restante           : num  20.3 53.5 13.9 45.7 67.2 72 69.5 19 64.6 19.9 ...
##  $ compartir                : Factor w/ 2 levels "NO","SI": 1 1 1 1 1 1 1 1 1 1 ...
##  $ lon                      : num  0 0 0 0 0 0 0 0 0 0 ...
##  $ lat                      : num  0 0 0 0 0 0 0 0 0 0 ...

Una vista inicial al set de datos nos permite observar:

head(clientes_telefonia)
ABCDEFGHIJ0123456789
 
 
estado
<fctr>
meses
<dbl>
ingresos
<dbl>
casa
<dbl>
precio_dispositivo
<dbl>
1VINCULADO2640748408.830e+081444153
2VINCULADO2385740881.745e+092157661
3RETIRADO387959939.500e+07452809
4RETIRADO3781150158.300e+072617184
5RETIRADO1981874996.770e+08666069
6VINCULADO1972502254.240e+08548116

Como se evidencia en la visualización cargada anteriormente, existe la variable compartir, la cual indica cuando un cliente ha decidido compartir su posición geográfica, una vez se ha caido su señal telefónica en una llamada. Procediendo con el objetivo, lo primero que procedemos a realizar es un filtrado de los clientes que SI desean compartir su ubicación y nos resta seleccionar solo las variables lat y lon, con el fin de agrupar por posición geográfica

  • Este aspetcto es realmente importante, si buscamos realizar un cluster por posición, debemos retirar todas las variables que no aporten información a la posición, de lo contratio, el algoritmo creara grupos erroneos
clientes_telefonia_compartido<- subset(clientes_telefonia, clientes_telefonia$compartir == "SI")
clientes_posicion<- clientes_telefonia_compartido[, 11:12]

Valores Atípicos Outliers

Revisamos la existencia de valores atípicos (outliers) a través del boxplot, en caso de que existan, estos se veran representados a distancias visualmente destacadas en el siguiente gráfico:

ggplot(data = clientes_posicion, mapping=aes(x = clientes_posicion$lon, y = clientes_posicion$lat)) +geom_boxplot() + 
  geom_jitter(width = 0.1) + theme_bw() + theme(legend.position = "none")

Verificación de valores nulos

colSums(is.na(clientes_posicion))
## lon lat 
##   0   0

Valores que no corresponda con el formato de cada variable:

colSums(clientes_posicion=="?")
## lon lat 
##   0   0

Representación Geográfica

Para la representación gráfica se ha usado la librería LeafLet, la cual tiene entre sus caracteristicas el agrupamiento de individuos y el cambio de indicador (icono de representación). A continuación se representan la posición geográfica de aquellos clientes que decidieron compartir su posición en el momento de perdida de señal:

leaflet( data = clientes_posicion) %>%
  addTiles() %>%
  addMarkers(~lon,~lat, clusterOptions = markerClusterOptions() )
32
134
304
6
14
338
393

Como se puede observar, tenemos 1221 pines en nuestro mapa, los cuales han sido agrupados por proximidad. Sin embargo, procederemos a aplicar algoritmos de clustering con el fin de conocer agrupaciones por caracteristicas similares de observaciones

Cluster K-MEANS

Se debe recordar que el algoritmo Kmeans es un algoritmo en el que se reparten las observaciones en k grupos de forma que la suma de las varianzas internas de todos sea la menos posible. Es decir, el mejor cluster es aquel cuya varianza interna (intra-cluster variation) es la más pequeña posible.

Uno de los factores más complejos en la aplicación de algoritmos de Clustering es identificar el K (numero de clusters). Se puede plantear que el mejor modelo es aquel que ofrece la menor suma de los cuadrados de las distancias de los puntos de cada grupo con respecto a su centro (withinss), con la mayor separacion entre centros de grupos (betweenss). Por lo que a continuación se probará con hasta 10 k posibles. Para esto, se hará uso de la función silhouette, la cual devuelve para cada muestra del set, el clúster dónde ha sido asignado, el clúster vecino y el valor de la silueta. Por lo tanto, calculando la media de la tercera columna podemos obtener una estimación de la calidad del agrupamiento:

d <- daisy(clientes_posicion) 
resultados <- rep(0, 10)
for (i in c(2,3,4,5,6,7,8,9,10))
{
  fit           <- kmeans(clientes_posicion, i)
  y_cluster     <- fit$cluster
  sk            <- silhouette(y_cluster, d)
  resultados[i] <- mean(sk[,3])
}

Una vez extraida la media del valor de la silueta ( mean(sk[,3]) ) y almacenando los valores de los clusters, se procede a gráficarlos para hacer más fácil la compresión. Es de mencionar que a mayor pico en el primer segmento, mejor es la calidad de agrupamiento:

plot(2:10,resultados[2:10],type="o",col="blue",pch=0,xlab="Numero de clusters",ylab="Silueta")

En este caso, a través de silhouette se han identificado 4 clusters optimos. Sin embargo, procederemos a realizar el calculo del numero K a través del método elbow. Este método consiste en, a través de la grafica representativa, seleccionar el valor en el que se encuentra la curva “codo” tras iterar el mismo set de datos por diferentes clusters.

resultados <- rep(0, 10)
for (i in c(2,3,4,5,6,7,8,9,10))
{
  fit           <- kmeans(clientes_posicion, i)
  resultados[i] <- fit$tot.withinss
}
plot(2:10,resultados[2:10],type="o",col="blue",pch=0,xlab="Número de clusters",ylab="tot.tot.withinss")  

Observando la gráfica anterior (como parte del método del codo) se puede identificar que en un k = 4, se empieza a estabilizar la gráfica.

  • En caso de querer profundizar en diferentes métodos para hallar el k más optimo, te invito a buscar los criterios de silueta media (asw) y Calinski-Harabasz (ch) de la función __ kmeansruns del paquete FPC__

Una vez identificado el mejor valor para k, procedemos a comparar visualmente los campos:

clientes.kmeans <- kmeans(clientes_posicion, 4)
ggplot(data = clientes_posicion, aes(x=lat, y=lon, color=as.factor(clientes.kmeans$cluster)))  +
      geom_point(size = 3) +
      labs(title = "Kmeans con k=4") +
      theme_bw() +
      theme(legend.position = "none")

Al aplicar clustering basado en el algoritmo kmeans, se observa un agrupamiento interesante, ya que refleja una identificacion de sectores geograficos en los cuales optimizar la red de telefonia. Ahora procedemos a identificar cada individuo en un cluster y a graficarlo. Para esto, usamos mutate y groupby:

clientes_posicion <- clientes_posicion %>% mutate(Grupo_Kmeans = clientes.kmeans$cluster)
clientes.grupo.kmeans <- clientes_posicion %>% group_by(Grupo_Kmeans) 
summarise(clientes.grupo.kmeans,observaciones = n())
ABCDEFGHIJ0123456789
Grupo_Kmeans
<int>
observaciones
<int>
1220
2425
3367
4209

Ubicacion Antenas por Clustering Kmeans

Ahora nos encargaremos de identificar los sectores en los cuales se propone el levantamiento de una antena, para esto obtenemos la media de cada cluster y graficamos en el mapa las coordenadas:

ubicacion_antena_kmeans <-  as.data.frame(clientes.kmeans$centers , row.names = FALSE) 
leaflet( data = ubicacion_antena_kmeans) %>%
  addTiles() %>%
  addMarkers(~lon,~lat, popup = ~lat, label = ~lon)

MCLUST

Recordemos que el clustering basado en modelos (MCLUST) considera que las observaciones proceden de diferentes distribuciones, utilizando el algoritmo de Expectation Maximization para identificar volumen, forma y orientacion de cada uno de los modelos del cluster.

Se procede a realizar la aplicación del algoritmo MCLUST. Lo primero que hacemos es identificar el k propuesto por mclust, para esto utilizamos la libreria mclust y proponemos utilizar hasta un maximo de 10 clusters:

clientes.mclust <- Mclust(data = clientes_posicion[,1:2], G = 1:10)
fviz_mclust(clientes.mclust, "BIC", palette = "jco")

Para nuestra sorpresa, mclust ha definido el maximo de 10 clusters, 6 mas que kmeans a traves del metodo de elbow. Ahora graficamos los clusters:

fviz_mclust(clientes.mclust, "uncertainty", palette = "jco")

Como se realizo con kmeans, agregamos una nueva variable con el grupo al que pertenece cada cliente como resultado de la aplicacion del algoritmo mclust.

clientes_posicion <- clientes_posicion %>% mutate(Grupo_Mclust = clientes.mclust$classification)
clientes.grupo.mclust <- clientes_posicion %>% group_by(Grupo_Mclust) 
summarise(clientes.grupo.mclust,observaciones = n())
ABCDEFGHIJ0123456789
Grupo_Mclust
<dbl>
observaciones
<int>
1191
289
389
4150
5151
6140
7114
8103
9106
1088

Ubicacion Antenas por Clustering MCLUST

Ahora nos encargaremos de identificar los sectores en los cuales se propone el levantamiento de una antena, para esto obtenemos la media de cada cluster y graficamos en el mapa:

ubicacion_antena_mclust <- as.data.frame(t(clientes.mclust$parameters[["mean"]]), row.names = FALSE)
leaflet( data = ubicacion_antena_mclust) %>%
  addTiles() %>%
  addMarkers(~ubicacion_antena_mclust$lon,~ubicacion_antena_mclust$lat)

DBSCAN Density Based Scan

Iniciamos recordando que este algoritmo identifica clusters de manera intuitiva gracias a la alta densidad de observaciones, es decir, de manera visual como lo haria el cerebro humano. Es importante mencionar que los algoritmos como kmeans, HC, medoids, entre otros, agrupan basado en formas esfericas o convexas que no presenten altos indices de ruido (outliers). Es aqui donde este algoritmo se destaca, ya que para para agrupar una observacion en un cluster, tienen que existir un minimo de observaciones dentro de este con un radio de proximidad pequeño, es decir que cada cluster estará separado por espacios vacios o pocas observaciones.

Ahora, entendiendo lo anterior, procedemos a aplicar el algoritmo DBSCAN, el cual requiere de dos parametros: * ϵ-neighborhood (eps): radio para definir el área de una observación vecina. * Minimum points (minPts): número mínimo de observaciones dentro de la región ϵ-neighborhood.

Para obtener el eps, se emplea la función kNNdistplot:

dbscan::kNNdistplot(clientes_posicion[,1:2], k = 10)

Como se observa, el punto de inflexión esta alrededor de los 0.005. Ahora aplicamos el algoritmo y graficamos los clusters

clientes.dbscan <- dbscan(clientes_posicion[,1:2], eps = 0.005, MinPts = 10)
fviz_cluster(clientes.dbscan, data = clientes_posicion[,1:2],  stand = FALSE,
             ellipse = TRUE, show.clust.cent = TRUE, ellipse.type = "t", 
             geom = "point",palette = "jco", ggtheme = theme_classic())

Como podemos observar, el algoritmo ha identificado 6 clusters, los cuales a simple vista parecen adecuados. Como se ha hecho con los anteriores algoritmos, procedemos a agregar la variable con el grupo correspondiente:

clientes_posicion <- clientes_posicion %>% mutate(Grupo_dbscan = clientes.dbscan$cluster)
clientes.grupo.dbscan<- clientes_posicion %>% group_by(Grupo_dbscan) 
summarise(clientes.grupo.dbscan,observaciones = n())
ABCDEFGHIJ0123456789
Grupo_dbscan
<int>
observaciones
<int>
097
1201
2325
376
4151
5270
6101

Se evidencian la cración de un cluster = 0, el cual hace referencia a 97 observaciones que no han sido categorizadas, las cuales pueden ser clasificados como outliers.

Ubicacion Antenas por Clustering DBSCAN

Ahora nos encargaremos de identificar los sectores en los cuales se propone el levantamiento de una antena, para esto se realiza el calculo de la media de cada cluster y se grafica en el mapa:

ubicacion_antena_dbcan <- summarise(
    select(
      group_by(clientes_posicion, Grupo_dbscan),
      lat:lon
    ),
    media.dbscan.lat = mean(lat, na.rm = TRUE),
    media.dbscan.lon = mean(lon, na.rm = TRUE)
  )
ubicacion_antena_dbcan <- ubicacion_antena_dbcan[2:7,]
leaflet( data = ubicacion_antena_dbcan) %>%
  addTiles() %>%
  addMarkers(~media.dbscan.lon,~media.dbscan.lat)