Segmentando las Estaciones de Servicio : ¿Dónde están las gasolineras mas económicas?

377731-campano-estacion-de-servicio-banner-01

Posted por @mirallesjm

a 19 de Enero, 2016

Motivación

La mayoría de las veces que pasamos por una estación de servicio, no podemos evitar girar la cabeza para revisar el panel de precios de la gasolina e intentar realizar una comparativa mental del resto de gasolineras conocidas. Establecer esta comparativa de precios resulta difícil principalmente porque no tenemos un único valor a comparar (Precio gasolina 95/98, gasóleo A/B, etc.) y dichos precios se mueven con un margen muy pequeño.

Nuestro objetivo pasa por visualizar esta información mediante un mapa que nos permita ver una primera distribución de precios y posteriormente aplicar técnicas de Machine Learning que nos permitan segmentar las estaciones de servicio vía precios y poder determinar grupos homogéneos de clasificación.

Análisis

Para realizar el estudio, disponemos de una base de datos de todas las estaciones de servicio de España (http://www.geoportalgasolineras.es/#/Descargas).

Lo primero que hacemos es descargarnos la información y leerla:

# Lectura del Dataset

Fuel <- read.table(“Gasolineras.txt”, header = TRUE, sep = “\t”, quote = “\””, dec = “,”,
na.strings = “NA”, fill = TRUE, comment.char = “”,stringsAsFactors = T)

# Dimension
dim(Fuel)

El dataset esta formado por 9.951 estaciones de servicio y 25 variables.

Observamos la estructura del dataset

# Recodificamos el nombre de los campos “Precio”

FuelPrecio <- select(Fuel, contains(“Precio”))

names(FuelPrecio) <- c(“PGasolina95″,”PGasoleoA”,”PGasoleoB”,”PBioEtanol”,”PNuevoGasoleoA”,

“PBioDiesel”,”PGasolina98″,”PGasNaturalComp”,”PGasNaturalLic”,

“PGasLicuPetro”)

# Estructura del dataset

str(FuelPrecio)

‘data.frame’: 9951 obs. of 10 variables:
$ PGasolina95 : num NA 1.17 1.23 1.16 1.22 …
$ PGasoleoA : num 1.06 1.05 1.12 1.03 1.12 …
$ PGasoleoB : num 0.726 NA NA NA NA NA 0.785 NA NA 0.8 …
$ PBioEtanol : num NA NA NA NA NA …
$ PNuevoGasoleoA : num NA 1.13 1.18 NA 1.18 …
$ PBioDiesel : num NA NA NA NA NA …
$ PGasolina98 : num NA 1.25 1.34 1.26 1.33 …
$ PGasNaturalComp: num NA NA NA NA NA NA NA NA NA NA …
$ PGasNaturalLic : num NA NA NA NA NA NA NA NA NA NA …
$ PGasLicuPetro : num NA NA NA NA NA NA NA NA NA NA …

summary(FuelPrecio)

PGasolina95 PGasoleoA PGasoleoB PBioEtanol
Min. :0.779 Min. :0.749 Min. :0.544 Min. :1.149
1st Qu.:1.195 1st Qu.:1.079 1st Qu.:0.729 1st Qu.:1.277
Median :1.239 Median :1.129 Median :0.789 Median :1.345
Mean :1.214 Mean :1.106 Mean :0.772 Mean :1.353
3rd Qu.:1.259 3rd Qu.:1.155 3rd Qu.:0.823 3rd Qu.:1.410
Max. :1.360 Max. :1.229 Max. :1.039 Max. :1.599
NA’s :476 NA’s :126 NA’s :7515 NA’s :9939
PNuevoGasoleoA PBioDiesel PGasolina98 PGasNaturalComp
Min. :0.814 Min. :0.909 Min. :0.890 Min. :0.689
1st Qu.:1.170 1st Qu.:1.039 1st Qu.:1.329 1st Qu.:0.853
Median :1.204 Median :1.089 Median :1.360 Median :0.898
Mean :1.184 Mean :1.081 Mean :1.340 Mean :0.859
3rd Qu.:1.220 3rd Qu.:1.126 3rd Qu.:1.389 3rd Qu.:0.898
Max. :1.329 Max. :1.232 Max. :1.555 Max. :0.995
NA’s :2834 NA’s :9873 NA’s :3429 NA’s :9914
PGasNaturalLic PGasLicuPetro
Min. :0.652 Min. :0.469
1st Qu.:0.689 1st Qu.:0.582
Median :0.689 Median :0.612
Mean :0.771 Mean :0.603
3rd Qu.:0.898 3rd Qu.:0.638
Max. :0.898 Max. :0.727
NA’s :9932 NA’s :9462

Podemos observamos la fuerte presencia de valores perdidos, dichos valores pueden ser un problema cuando se analiza un conjunto de datos.

En algunos casos, si el numero de datos perdidos es relativamente pequeño la eliminación de ellos puede ser una estrategia a seguir. En nuestro caso debido a la presencia significativa de valores perdidos, optamos por la opción de estimarlos mediante técnicas de Machine Learning.

Tratamiento de valores Missing

Una primera visualización de los valores perdidos nos la proporciona el paquete VIM.

library(VIM)

aggr_plot <- aggr(Fuel, numbers=TRUE, sortVars=TRUE, labels=names(Fuel), 
cex.axis=.5, gap=3, ylab=c("Histograma de valores perdidos","Pattern"))

rplot05

La visualización nos permite ver que casi un 75% de los registros no presentan missing, el 22% de los missing es debido a la variable “Precio Bio Etanol”.

Igualmente podemos realizar otra aproximación visual comparando dos variables:

marginplot(FuelPrecio[,c(“PGasolina95″,”PGasolina98”)])

rplot

Aquí podemos ver la distribución de valores perdidos del “Precio de la gasolina 98” con falta de valores del “Precio de la gasolina 95”. La parte azul nos muestra la distribución del resto de puntos.

Imputación de Misssing

A través de la librería MICE podemos asignar valores perdidos mediante técnicas predictivas.

library(mice)

Fuelmice<- mice(FuelPrecio,m=5,maxit=50,meth='rf',seed=500)
completedFuel <- complete(Fuelmice,1)
summary(completeFuel)

PGasolina95 PGasoleoA PGasoleoB
Min. :0.779 Min. :0.749 Min. :0.544
1st Qu.:1.189 1st Qu.:1.079 1st Qu.:0.720
Median :1.235 Median :1.129 Median :0.789
Mean :1.213 Mean :1.106 Mean :0.768
3rd Qu.:1.259 3rd Qu.:1.155 3rd Qu.:0.820
Max. :1.360 Max. :1.229 Max. :1.039
PBioEtanol PNuevoGasoleoA PBioDiesel
Min. :1.149 Min. :0.814 Min. :0.909
1st Qu.:1.283 1st Qu.:1.155 1st Qu.:1.064
Median :1.345 Median :1.194 Median :1.118
Mean :1.346 Mean :1.174 Mean :1.108
3rd Qu.:1.399 3rd Qu.:1.219 3rd Qu.:1.155
Max. :1.599 Max. :1.329 Max. :1.232
PGasolina98
Min. :0.890
1st Qu.:1.319
Median :1.359
Mean :1.338
3rd Qu.:1.384
Max. :1.555

En este caso utilizamos como modelo de imputación un Random Forest.

Fuel <- select(Fuel, -contains(“Precio”), completeFuel)

Clustering

Determinaremos una clusterización que nos permita comparar estaciones de servicio similares y diferentes entre si, de cara a poder seleccionar y encontrar estaciones de servicio adaptadas a nuestras necesidades.

Para ello determinaremos con las variables numéricas, una vez escaladas, las componentes principales y determinaremos el número optimo de segmentos.

# Seleccionamos los atributos numéricos y escalamos

Fuel_num

Fuel_scale # Aplicamos análisis de componentes principales
pc plot(pc)
plot(pc, type = “l”)
summary(pc)
# Obtenemos componentes principales
pc summary(pc)

# Primeras componentes principales
comp

# Plot
plot(comp, pch = 16, col= rgb(0,0,0,0.5))
wss

for (i in 2:15)

wss[i]

plot(1:15, wss, type = “b”, xlab = “Number of Cluster”, ylab = “Within groups sum of squares”)

# K-Means Clustering con 4 clusters

Fuel_Cluster Fuel_Cluster
aggregate(Fuel[,16:22], by=list(Fuel_Cluster$cluster), FUN = mean)

library(ggplot2)

Fuel$cluster

Fuel$PC1 Fuel$PC2

ggplot(Fuel, aes(PC1, PC2, color = Fuel$cluster)) +
geom_point()

El siguiente paso es ponerle cara a los clusters:

aggregate(Fuel[,16:22], by=list(Fuel_Cluster$cluster), FUN = mean)

Group.1 PGasolina95 PGasoleoA PGasoleoB PBioEtanol PNuevoGasoleoA
1 1 0.9402179 0.8586645 0.6861961 1.291664 0.9427451
2 2 1.2504438 1.1443182 0.8026884 1.472251 1.2108244
3 3 1.1654730 1.0503863 0.6936435 1.304441 1.1177545
4 4 1.2453041 1.1389383 0.7956955 1.301777 1.2053461
PBioDiesel PGasolina98
1 1.045405 1.049529
2 1.136626 1.372269
3 1.044700 1.299260
4 1.132222 1.368115

En este caso los cluster1 corresponden con estaciones de servicio económicas frente al cluster2 donde los precios son mucho mas altos.

rplot06

Visualización

Una de las formas mas atractiva de visualizar los datos es a través de los mapas. En este caso utilizaremos todo el potencial de  las librerías ggmap y ggplot2.

En nuestro Dataset, ya disponemos tanto de la longitud como de la latitud que nos permitirá georefenciar la estación de servicio y de esta forma visualizar su Cluster en mapa.

library(ggmap)
library(ggplot2)
Spain.map = get_map(location = “Spain”, zoom = 6, color=”bw”)
p p p p + theme(axis.title=element_blank(),
axis.text=element_blank(),
axis.ticks=element_blank()) + ggtitle(“Clusters Estaciones Servicio”)

rplot04

Podemos visualizar los clusters de manera individual:

rplot09

Podemos hacer zoom y ver el caso particular de la provincia de Almería.

rplot11

Reunión: 12 de enero del 2017

El próximo  12 de Enero a las 20:oo h recibiremos el año con una nueva reunión  del “Grupo de Usuarios de R de Almería”, en la que tendremos dos presentaciones muy interesantes.

usuariosr2017

El lugar elegido para las presentaciones son las instalaciones  Workspace Coworking en Plaza Administración Vieja 1, 1º izq.

¡Os esperamos!

Web Scraping, PCA y K-means para sacar todo el potencial a @LaLiga

 a 11 de Octubre, 2016

Introducción

Esta claro que el mundo de fútbol levanta pasiones, el fútbol y todo lo que lo rodeada, entre ellas algunas web que nos permiten gestionar de manera virtual a nuestro equipo, además de recoger los puntos de la jornada sirve para confeccionar el once inicial.

En este caso, nuestro objetivo es poder llegar a segmentar a los jugadores de la liga profesional de fútbol en función a una serie de inputs que la web  pone a nuestra disposición, y de esta forma poder ayudar en la toma decisiones a la hora de gestionar mi equipo. http://www.comuniazo.com/

liga

Análisis

En primer lugar necesitamos extraer la información necesaria para seleccionar los atributos que determinaran la segmentación. Para ello, realizamos scraping a la web “comuniazo” a través de la siguiente url:

url <- "http://www.comuniazo.com/comunio/jugadores"

La libreria XML nos permite extraer información de la página web mediante la función readHTMLTable

library(XML)

jugadores= readHTMLTable(jugadores,
 stringsAsFactors = T,
 colnames = c("Posicion","Equipo","Jugador","Puntos","Media","Puntos_Casa","Media_Casa","Puntos_Fuera","Media_fuera", "Valor"),
 colClasses = c("character","character","character","FormattedNumber","FormattedNumber","FormattedNumber","FormattedNumber","FormattedNumber","FormattedNumber"))

El dataset esta formado por 504 registros y 14 variables candidatas.

Análisis de componentes principales

A continuación vamos a aplicar una de las técnicas de reducción de dimensionalidad , aplicamos análisis de componentes principales de los datos en R, para ello previamente escalaremos el dataset:

# Seleccionamos los atributos numéricos y escalamos

jugadores_num <- jugadores[,4:10]
jugadores_scale <- scale(jugadores_num)

# Aplicamos análisis de componentes principales
pc <- princomp(jugadores_scale)
plot(pc)
plot(pc, type = "l")
summary(pc)

rplot
rplot01Seleccionando dos componentes principales conseguimos explicar el 86% de la varianza.

# Obtenemos componentes principales
pc <- prcomp(jugadores_scale)
summary(pc)

Importance of components:
 Comp.1 Comp.2 Comp.3 Comp.4
Standard deviation 2.2477665 0.9923867 0.71057691 0.50588385
Proportion of Variance 0.7232142 0.1409699 0.07227477 0.03663246
Cumulative Proportion 0.7232142 0.8641840 0.93645881 0.97309127
 Comp.5 Comp.6 Comp.7
Standard deviation 0.37326927 0.22058428 0
Proportion of Variance 0.01994385 0.00696488 0
Cumulative Proportion 0.99303512 1.00000000 1

# Primeras componentes principales
comp <- data.frame(pc$x[,1:2])

rplot02

Análisis Cluster

Vamos a segmentar con la técnica más simple, para ello utilizaremos k-medias en R.
En primer lugar, ¿cómo determinamos el número de grupos? El método más simple es mirar a la suma de cuadrados dentro de los grupos y escoger el “punto” en la gráfica, de forma similar a como ocurre con el gráfico de sedimentación que hicimos para el PCA previamente. A continuación mostramos el código utilizado:

# Plot
plot(comp, pch = 16, col= rgb(0,0,0,0.5))


wss <- (nrow(jugadores_scale)-1)*sum(apply(jugadores_scale,2,var))

for (i in 2:15) 
 
 wss[i] <- sum(kmeans(jugadores_scale, centers =i)$withinss)

plot(1:15, wss, type = "b", xlab = "Number of Cluster", ylab = "Within groups sum of squares")

rplot03

# K-Means Clustering con 6 clusters

jugadores_Cluster <- kmeans(comp, 6, nstart = 25, iter.max = 1000)
jugadores_Cluster
aggregate(jugadores[,4:10], by=list(jugadores_Cluster$cluster), FUN = mean)

K-means clustering with 6 clusters of sizes 72, 114, 71, 64, 144, 39

Cluster means:
 PC1 PC2
1 -1.51933814 -1.1588460
2 0.81871056 -0.4593748
3 -0.09203462 0.9141368
4 -2.37569832 0.9286046
5 2.47658186 0.1106443
6 -4.66639217 -0.1143984

Clustering vector:
 [1] 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 4 6 6 6 6 6 6 6 6 4 6 4 6 6 6 6 6
 [34] 6 6 4 4 6 6 1 4 4 6 6 6 4 1 4 1 4 4 4 1 4 1 4 4 1 4 4 4 1 4 4 6 1
 [67] 4 1 4 1 1 1 4 4 4 4 4 4 4 1 6 1 4 4 4 1 1 4 4 4 1 4 4 4 4 1 4 4 4
[100] 1 4 4 4 4 1 4 4 1 4 1 4 4 1 1 4 4 1 1 4 1 1 1 1 1 1 4 4 1 1 4 4 1
[133] 4 3 3 3 1 3 1 1 3 3 1 3 1 4 1 1 1 3 1 1 1 3 1 4 3 3 1 1 3 3 1 1 1
[166] 1 2 1 3 3 2 1 3 3 1 3 1 3 3 1 3 1 1 1 1 3 3 3 2 3 2 3 3 2 4 1 1 3
[199] 1 2 3 3 1 2 1 2 2 3 2 3 3 3 2 2 2 2 3 3 2 1 1 3 2 3 3 3 3 3 4 4 3
[232] 1 2 2 2 3 3 2 2 3 3 2 2 2 3 2 2 2 2 1 3 3 2 2 3 3 2 2 2 2 2 3 2 2
[265] 2 3 2 3 2 2 2 2 2 2 2 2 2 2 2 3 3 2 3 2 2 2 2 2 2 2 2 2 2 3 2 2 2
[298] 3 3 3 2 2 2 2 2 3 3 3 3 2 1 3 2 2 2 2 2 2 2 2 5 2 2 3 5 2 2 2 3 2
[331] 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 5 2 5 5 2 2 5 3 5 2 2 2 2 5 5 5 5 2
[364] 5 2 2 2 2 2 5 2 5 2 2 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
[397] 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
[430] 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
[463] 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5 5
[496] 5 5 5 5 5 5 5 5 5

Within cluster sum of squares by cluster:
[1] 76.21358 75.80518 55.22837 67.37217 75.01832 118.73351
 (between_SS / total_SS = 84.6 %)

Available components:

[1] "cluster" "centers" "totss" "withinss" 
[5] "tot.withinss" "betweenss" "size" "iter" 
[9] "ifault"

Una vez calculados los clusters correspondientes los visualizaremos en función de las dos componentes principales utilizadas:

library(ggplot2)

jugadores$cluster <- as.factor(jugadores_Cluster$cluster)

jugadores$PC1 <- comp$PC1
jugadores$PC2 <- comp$PC2

ggplot(jugadores, aes(PC1, PC2, color = jugadores$cluster)) +
 geom_point()

rplot04

El siguiente paso sería identificar los segmentos y establecer una etiqueta que los identifique y poder gestionar la estrategia.

Hasta aquí llega este post. ¡Espero que le haya gustado! Cualquier pregunta o comentario, no dude en dejar un comentario o acercarse a mí en Twitter: @mirallesjm

Nuevo Reto – Satisfacción del cliente

Desde Usuarios de R Almería os proponemos abordar el reto propuesto por http://www.kaggle.com sobre la satisfacción del cliente en el sector bancario.

Nuestro objetivo es poder trabajar dicho caso de estudio, ya sea a nivel individual o grupo, con el fin de poder mostrar resultados y compartir impresiones en la próxima charla de Usuarios de R Almería.

Os dejamos el link para que podáis consultarlo y tener un mayor nivel de detalle https://www.kaggle.com/c/santander-customer-satisfaction

Animo!