1 Analisis sobre todos clientes (Feature Engineering)

Se presenta información de los clientes de manera individual, sin tener en cuenta el tipo de licencia que poseen. Se han calculado los siguientes atributos:

  1. Tiempo transcurrido desde la primera la primera operacion hasta la ultima. Se calcula como la diferencia entre la ultima y la primera facturacion
  2. Monto total de facturacion
  3. Numero total de operaciones realizadas
  4. Numero de veces que se realización un aumento o renovacion de licencia (increase|renew ) para server o sp
  5. Numero total de operaciones de alta importancia
  6. Numero de cancelaciones
  7. Numero de servidores por clientes
  8. Facturacion mensual (esto ultimo elimina la influencia del tiempo en el monto de facturación)

Se filtran los clientes que tienen menos de 12 meses en la empresa y que no hayan realizado al menos una renovacion de servicio.

# Se calcula el tiempo entre facturaciones en meses
client_info_elapsed_time=invoice_data %>%  group_by(client_id) %>% summarise(firstdate=min(timestamp),lastdate=max(timestamp)) %>% mutate(elapsed_time=(lastdate-firstdate)/60/60/24/30) %>% select(client_id,elapsed_time)

# Aquellos clientes con una operacion se los considera con un mes como tiempo minimo de permanencia  
client_info_elapsed_time$elapsed_time=ifelse(client_info_elapsed_time$elapsed_time<1,1,client_info_elapsed_time$elapsed_time)

# Se calcula el numero de operaciones que se facturaron
client_info_operations=invoice_data  %>% group_by(client_id) %>% 
  summarise(total_operation_count_per_client=n_distinct(invoice_id))

 # Numero de veces que un cliente realizo un increase o upgrade
number_of_increase_server=invoice_data %>% filter(invoice_item_kind=='increase'& invoice_item_article=='server') %>%
  group_by(client_id) %>% 
  summarise(n_server_increase=n())


number_of_renew_server=invoice_data %>% filter(invoice_item_kind=='renew'& invoice_item_article=='server') %>%
  group_by(client_id) %>% 
  summarise(n_server_renew=n())

number_of_renew_sp=invoice_data %>% filter(invoice_item_kind=='renew'& invoice_item_article=='supercache') %>%
  group_by(client_id) %>% 
  summarise(n_sp_renew=n())

number_of_increase_sp=invoice_data %>% filter(invoice_item_kind=='increase'& invoice_item_article=='supercache') %>%
  group_by(client_id) %>% 
  summarise(n_sp_increase=n())

number_of_remove=invoice_data %>% filter(invoice_item_kind=='remove') %>%
  group_by(client_id) %>% 

  summarise(n_remove=n())

server_type_by_client=invoice_data %>% group_by(client_id,server_id) %>% select(client_id,server_number_of_clients) %>% ungroup() %>% distinct() %>% select(client_id,server_id,server_number_of_clients) 

server_type_by_client=server_type_by_client %>% filter(!is.na(server_id))
server_type_by_client=reshape2::dcast(server_type_by_client,client_id~server_number_of_clients)
#server_type_by_client=server_type_by_client[,1:ncol(server_type_by_client)-1]

number_of_servers_per_client = invoice_data %>% group_by(client_id) %>% summarise(n_servers=n_distinct(server_id,na.rm = TRUE))

# Extrayendo informacion de facturacion por cliente
client_invoice_amount= invoice_data %>% group_by(client_id) %>% 
  summarise(total_invoice_amount=sum(invoice_item_amount)/1000)

# Extrayendo informacion personal del cliente
client_personal_info = invoice_data  %>% 
  select(client_id,client_name,client_company_name,client_country_name,client_geo_provice) %>% 
  distinct()

client_info=
  full_join(client_info_operations,client_info_elapsed_time,by="client_id") %>% 
  full_join(client_invoice_amount,by="client_id")  %>% 
   full_join(client_personal_info,by="client_id")  %>%
   full_join(number_of_increase_server,by="client_id") %>%
   full_join(number_of_renew_server,by="client_id") %>%
   full_join(number_of_increase_sp,by="client_id") %>%
   full_join(number_of_renew_sp,by="client_id") %>%
   full_join(number_of_servers_per_client,by="client_id") %>%
   full_join(number_of_remove,by="client_id") %>% 
   full_join(server_type_by_client,by="client_id") 

client_info = client_info %>% mutate( n_server_increase = replace(n_server_increase, is.na(n_server_increase), 0)) %>%
  mutate( n_server_renew = replace(n_server_renew, is.na(n_server_renew), 0)) %>%
  mutate( n_sp_increase = replace(n_sp_increase, is.na(n_sp_increase), 0)) %>%
  mutate( n_sp_renew = replace(n_sp_renew, is.na(n_sp_renew), 0)) %>%
  mutate( n_remove = replace(n_remove, is.na(n_remove), 0)) 
  
# Se calcula promedio mensual de facturacion por cliente
client_info$avg_monthly_invoice=(client_info$total_invoice_amount/client_info$elapsed_time)

client_info=client_info %>% rowwise() %>% mutate(n_top_operations=sum(n_server_increase,n_server_renew,n_sp_increase,n_sp_renew,na.rm=T))

# Solo consideramos aquellos que han estado por lo menos 12 meses y hayan hecho al menos un renew
client_info=client_info %>% filter(elapsed_time >12 & (n_sp_renew >=1 | n_server_renew>=1))
client_info$client_id=factor(client_info$client_id)

1.1 Dataset con información de clientes

library(DT)
datatable(client_info[,-c(5,6,7,8)], filter="bottom", options = list(
    search = list(regex = TRUE, caseInsensitive = FALSE),
    pageLength = 5
  ), class = 'cell-border stripe', extensions = 'Buttons'
  )

1.2 Numero de operaciones y tiempo transcurrido desde el inicio de operaciones para cada cliente

El siguiente grafico muestra la relación entre el tiempo transcurrido en la empresa y el monto total de facturación por cada cliente. Cada punto representa un cliente. Ademas, el color indica el promedio de facturacion mensual de cada cliente y el tamaño, la cantidad de operaciones de alta importancia (server o superache increase o renew) . Finalmente, la recta de color cyan, muestra la tendencia observada.

gg=ggplot(client_info,aes(x=elapsed_time,y=total_invoice_amount))+
  geom_jitter(aes(colour=avg_monthly_invoice,size=n_top_operations, text = paste0("client_name:", client_name)),alpha=0.8)+
  scale_x_continuous('Tiempo de permanencia en la empresa [meses]', expand=c(0.01,0.3 ) )+ylab("monto total de facturacion [Ku$S]")+
  labs(colour = 'Facturacion promedio mensual', size = 'numero de renovaciones y upgrades (increase|renew)')+
  scale_color_gradient2(low = "cyan", mid='blue',high = "red")+
  ggtitle("Información por cliente (sin considerar tipo de server)")+  
  theme(plot.title = element_text(lineheight=3.5, face="bold"))+
  scale_size(range = c(1, 10))+
  geom_smooth(aes(x=elapsed_time,y=total_invoice_amount),method='lm',colour='skyblue',se = F)
#  facet_wrap(~client_country_name)
#gg

ggplotly(gg,layerData=1)

El indicador de facturacion mensual parece ser un buen discriminador, se pueden diferenciar al menos 4 tipos de clientes sin importar el tiempo transcurrido

El siguiente boxplot muestra la distribucion del indicador de facturacion mensual. En base a este grafico se pueden establecer los rangos que definen a las 4 categorias y considerar en una 5ta a aquellos clientes que estan fuera de lo normal. (ver Code)

par(mfrow=c(3,1))
bwplot(~avg_monthly_invoice,data=client_info,horizontal = T,xlab="promedio de facturacion mensual")

client_info=client_info %>% mutate(client_category=ifelse(avg_monthly_invoice >0 & avg_monthly_invoice <=0.0338,1,
                                       ifelse(avg_monthly_invoice >0.0338 & avg_monthly_invoice <=0.057,2,
                                       ifelse(avg_monthly_invoice >0.057 & avg_monthly_invoice <=0.1,3,
                                       ifelse(avg_monthly_invoice >0.1 & avg_monthly_invoice <=0.2,4,
                                       ifelse(avg_monthly_invoice>0.2 ,5,NA)))    
                                              )))
client_info$client_category=factor(client_info$client_category)
summary(client_info$avg_monthly_invoice)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## 0.006294 0.038820 0.057850 0.080530 0.106600 0.455600

2 Categorias de clientes

Se definen 5 categorias de clientes en base a su facturación mensual. Se observa que las categorias 3 y 4 representan el mayor porcentaje de la facturacion .

#client_info %>% group_by(client_category) %>% summarise(mean=mean(avg_monthly_invoice),sd=sd(avg_monthly_invoice))
#shapiro.test(unlist(client_info %>% filter(client_category==3) %>% select(avg_monthly_invoice)))
#densityplot(~avg_monthly_invoice,data=client_info %>% filter(client_category==4))
#shapiro.test(unlist(client_info %>% filter(client_category==4) %>% select(avg_monthly_invoice)))

info_by_client_category_percent=client_info %>% 
  group_by(client_category) %>% 
  summarise(client_category_invoice_total=sum(total_invoice_amount)) %>%  
  mutate(percent=(client_category_invoice_total/sum(client_category_invoice_total))*100)

barchart(percent~factor(client_category),data=info_by_client_category_percent,xlab='categoria cliente',ylab='porcentaje de facturacion total',main="Distribución de la facturacion total por tipo de cliente",scales=list(x=list(rot='45')),)

2.0.1 Facturacion promedio por mes por categoria de cliente

bwplot(~avg_monthly_invoice|client_category,data=client_info,horizontal = T,
         panel = function(x, y, ...) {
        panel.bwplot(x, y, ...)
        nn <- table(x)
        panel.text(nn, x = seq_along(nn),
               y = current.panel.limits()$y[1], pos = 3,cex=0.5)
  },scales=list(x = list(relation = "free")),xlab="promedio de facturacion mensual",cex=0.4)

cbind(cat1=summary(client_info %>% filter(client_category==1) %>% select(avg_monthly_invoice)),
cat2=summary(client_info %>% filter(client_category==2) %>% select(avg_monthly_invoice)),
cat3=summary(client_info %>% filter(client_category==3) %>% select(avg_monthly_invoice)),
cat4=summary(client_info %>% filter(client_category==4) %>% select(avg_monthly_invoice)),
cat5=summary(client_info %>% filter(client_category==5) %>% select(avg_monthly_invoice)))
##  avg_monthly_invoice  avg_monthly_invoice avg_monthly_invoice
##  "Min.   :0.006294  " "Min.   :0.03390  " "Min.   :0.05772  "
##  "1st Qu.:0.017157  " "1st Qu.:0.03970  " "1st Qu.:0.06325  "
##  "Median :0.022034  " "Median :0.04608  " "Median :0.07350  "
##  "Mean   :0.021332  " "Mean   :0.04508  " "Mean   :0.07573  "
##  "3rd Qu.:0.026798  " "3rd Qu.:0.05048  " "3rd Qu.:0.08950  "
##  "Max.   :0.033708  " "Max.   :0.05631  " "Max.   :0.09918  "
##  avg_monthly_invoice avg_monthly_invoice
##  "Min.   :0.1003  "  "Min.   :0.2036  " 
##  "1st Qu.:0.1170  "  "1st Qu.:0.2253  " 
##  "Median :0.1338  "  "Median :0.2479  " 
##  "Mean   :0.1364  "  "Mean   :0.2663  " 
##  "3rd Qu.:0.1535  "  "3rd Qu.:0.2779  " 
##  "Max.   :0.1983  "  "Max.   :0.4556  "

2.1 Caracterización de las categorias de clientes

Definidas estas primeras categorias, resulta importante caracterizar cada una de ellas, para tal fin, se analizan diferentes atributos de interes.

Los siguientes boxplot muestran la distribucion de otros atributos de interes para cada categoria de clientes En particular se presenta información sobre:

  1. Total de facturación

  2. Tiempo transcurrido

  3. Numero de operaciones de alta importancia (supercache,server,renew,increase)

  4. Numero de operaciones de renovacion de server

2.1.1 Total de facturacion por categoria de cliente

bwplot(total_invoice_amount~client_category,data=client_info,horizontal = F,
         panel = function(x, y, ...) {
        panel.bwplot(x, y, ...)
        nn <- table(x)
        panel.text(nn, x = seq_along(nn),
               y = current.panel.limits()$y[1], pos = 3,cex=0.5)
  },ylab="monto total de facturación")

La facturación total presenta diferencias y se observa un comportamiento esperado donde los clientes de las categorias mas altas son los que mayor monto de facturacion tienen.

2.1.2 Tiempo transcurrido en contacto con la empresa por categoria de cliente

par(mfrow=c(1,3))

bwplot(elapsed_time~client_category,data=client_info,horizontal = F,
        panel = function(x, y, ...) {
        panel.bwplot(x, y, ...)
        nn <- table(x)
        panel.text(nn, x = seq_along(nn),
               y = current.panel.limits()$y[1], pos = 3,cex=0.5)
  },ylab="tiempo de permanencia en la empresa")

histogram(~elapsed_time,data=client_info %>% filter(client_category==1),main="Categoria 1")

histogram(~elapsed_time,data=client_info %>% filter(client_category==2),main="Categoria 2")

histogram(~elapsed_time,data=client_info %>% filter(client_category==3),main="Categoria 3")

histogram(~elapsed_time,data=client_info %>% filter(client_category==4),main="Categoria 4")

Sin embargo,el tiempo no parece ser un atributo significantivo para caracterizar las categorias de clientes. En principio se podria esperar que los clientes que mas facturación mensual tienen, sean aquellos que mas tiempo han permanecido con la empresa. Esto sin embargo no se observe en el grafico, ya que para todas las categorias de clientes se observan una variacion similar en el tiempo.

2.1.3 Numero de operaciones de importancia por categoria de cliente

bwplot(n_top_operations~client_category,data=client_info,horizontal = F,
         panel = function(x, y, ...) {
        panel.bwplot(x, y, ...)
        nn <- table(x)
        panel.text(nn, x = seq_along(nn),
               y = current.panel.limits()$y[1], pos = 3,cex=0.5)
  },ylab="numero de operaciones de importancia")

histogram(~n_top_operations,data=client_info %>% filter(client_category==1),,main="Categoria 1")

histogram(~n_top_operations,data=client_info %>% filter(client_category==2),,main="Categoria 2")

histogram(~n_top_operations,data=client_info %>% filter(client_category==3),,main="Categoria 3")

histogram(~n_top_operations,data=client_info %>% filter(client_category==4),,main="Categoria 4")

El numero de operaciones de alta importancia sigue una tendencia similar a la de la facturación total, entre los clientes del tipo 2 y 3 no se observan tantas diferencias en general, aunque si se observa un numero de outliers. Queda entonces discriminar el tipo de operaciones de alta importancia.

2.1.4 Numero de renovaciones de licencias de servidores por categoria de cliente

bwplot(n_server_renew~client_category,data=client_info,horizontal = F,ylab="numero de renovaciones de licencias")

El numero de renovaciones de no ofrece diferencias salvo para las categorias 1 y 5. Las restantes (2,3,4) parece tener comportamiento similar.

2.1.5 Numero de licencias por categoria de cliente

bwplot(n_servers~client_category,data=client_info,horizontal = F,
         panel = function(x, y, ...) {
        panel.bwplot(x, y, ...)
        nn <- table(x)
        panel.text(nn, x = seq_along(nn),
               y = current.panel.limits()$y[1], pos = 3,cex=0.5)
  },ylab="numero de licencias")

El numero de licencias ofrece algunas diferencias significativas, sin embargo entre la categoria 3 y 4 no se observan practicamente diferencias mas alla de los outliers de la categoria 3. Cabe entonces preguntarse si la diferencia en la facturacion mensual radica el tipo especifico de licencia.

2.1.6 Tipo de licencias por categoria de cliente

Para analizar las caratecteristicas de los clientes en base al tipo y numero de licencia que estos poseen se grafica la información de cliente por tipo de licencia. Donde cada punto representa un tipo de licencia para un cliente dado. El eje X muestra los distintos tipos de licencias y el eje la facturación mensual. Las categorias son diferencias por color. Los distintos tipos de licencias para un mismo cliente se ordenan sobre el eje Y. Por ultimo si posee mas de una licencia de un mismo tipo se incremente el tamaño del punto.

heat=client_info[,c(1,2,9,13,15:28,29,30,31)]
heat=reshape2::melt(heat[,1:21],id.vars=c("avg_monthly_invoice","client_category","client_id","n_servers","n_server_increase","n_top_operations","total_operation_count_per_client"),variable.name="server_type")
#heat$client_category=factor(heat$client_category)
#heat$server_type=factor(heat$server_type)

gg=ggplot(heat %>% filter(value!=0),aes(y=avg_monthly_invoice,x=server_type))+
  geom_jitter(aes(size=value,colour=client_category,text=paste("client_id",client_id,"<BR>n_servers",n_servers,"<BR>n_server_increase",n_server_increase,"<BR>n_top_operations",n_top_operations,"<BR>n_total_operations",total_operation_count_per_client))    ,width = 0.05,alpha=0.5) + 
  ylab("monto total de facturacion mensual [Ku$S]")+
  xlab("tipo de servidor")+
  labs(size = 'numero de licencias',color="categoria de clientes")+
  ggtitle("Información por cliente por tipo de licencia")+  
  theme(plot.title = element_text(lineheight=3.5, face="bold"))+
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

ggplotly(gg)

En general, se observa una ligera tendencia en donde los montos de facturación mensuales tienden a estar correlacionados con el tipo de licencia. Por ejemplo, la categoria 1 solo tienen licencias que van de 50 a 350, mientras que la cat 2 y la 3 tienen de 50 a 1500, la categoria 4 llega en algunos casos a 3000 y la categoria 5 llega a 4000 usuarios. SIn embargo no parece ser la unica explicación para la variación en los montos mensuales de facturación.

Los diferentes tipos de licencias que puede poseer un mismo cliente influyen en el monto mensual de un cliente. Es decir que la catgoria de un cliente esta influenciada por los distintos tipos de licencias que poseen. Aunque hay casos dificiles de explicar como el cliente 29 y 44. Los cuales estan en diferentes categorias y poseen licencias parecidas. Luego de observar las diferencias de ambos clientes se observan que el cliente 44 (cat 4) posee una licencia de 500 usuarios, y por otro lado el cliente 29 (cat 3) tiene licencias 150 a 350 usuarios, lo que se suma el hecho de que alguna de estas licencias fue incrementada recientemente.

clients=client_info %>% filter(client_id==44 | client_id==29) 
clients=clients[,c(2,3,4,9:13,15:28,31)]
datatable(clients, filter="bottom", options = list(
    search = list(regex = TRUE, caseInsensitive = FALSE),
    pageLength = 5
  ), class = 'cell-border stripe', extensions = 'Buttons'
  )

3 Analisis mediante arboles de decisión

El analisis anterior muestra que la categoria de un cliente esta influenciada por diversas variables que deben analizarse en conjunto. Los arboles de decision son una herramienta que permite establecer diferentes combinaciones de variables/atributos utilizando un enfoque Divide & Conquer. Se seleccionan aquellos atributos y valores que tienen mayor impacto a la hora de discriminar entre las posibles categorias. Cada una de la hojas del arbol termina siendo una regla que explica una categoria con una probabilidad determininada.

library(rpart)
library(rattle)
library(rpart.plot)
library(rpart.utils)
#client_info_tree=client_info[,c(9:13,15:28,29,31)]
#formula <- as.formula(avg_monthly_invoice~.)

client_info_tree=client_info[,c(9:13,15:28,31)]
formula <- as.formula(client_category~.)
tree=rpart(formula,data=client_info_tree,control = rpart.control(minsplit=6, cp=0.001))
prp(tree,cex=0.85 )

printcp(tree)
## 
## Classification tree:
## rpart(formula = formula, data = client_info_tree, control = rpart.control(minsplit = 6, 
##     cp = 0.001))
## 
## Variables actually used in tree construction:
##  [1] 100               1000              150              
##  [4] 1500              250               2500             
##  [7] 350               50                500              
## [10] 800               n_server_increase n_server_renew   
## [13] n_servers         n_sp_increase     n_sp_renew       
## 
## Root node error: 190/270 = 0.7037
## 
## n= 270 
## 
##           CP nsplit rel error  xerror     xstd
## 1  0.1105263      0   1.00000 1.00000 0.039490
## 2  0.0631579      1   0.88947 0.88947 0.041847
## 3  0.0421053      2   0.82632 0.86842 0.042160
## 4  0.0263158      3   0.78421 0.82632 0.042663
## 5  0.0157895      4   0.75789 0.79474 0.042936
## 6  0.0131579      6   0.72632 0.80526 0.042855
## 7  0.0105263      8   0.70000 0.80526 0.042855
## 8  0.0078947     18   0.58947 0.78421 0.043008
## 9  0.0052632     20   0.57368 0.81579 0.042764
## 10 0.0026316     42   0.45789 0.85789 0.042301
## 11 0.0013158     44   0.45263 0.84737 0.042432
## 12 0.0010000     48   0.44737 0.84737 0.042432
#plot(tree,uniform=T,main="Regression Tree for Customer categories")
#text(tree,cex=0.7,minlength=20,all=T,use.n=T,xpd=T)
#asRules(tree)
client_info_tree=client_info[,c(9:13,15:28,29)]
formula <- as.formula(avg_monthly_invoice~.)
tree=rpart(formula,data=client_info_tree,control = rpart.control(minsplit=15, cp=0.001))
prp(tree,cex=0.6 )

printcp(tree)
## 
## Regression tree:
## rpart(formula = formula, data = client_info_tree, control = rpart.control(minsplit = 15, 
##     cp = 0.001))
## 
## Variables actually used in tree construction:
##  [1] 100               1000              150              
##  [4] 1500              250               350              
##  [7] 50                500               800              
## [10] n_server_increase n_server_renew    n_servers        
## [13] n_sp_renew       
## 
## Root node error: 1.143/270 = 0.0042335
## 
## n= 270 
## 
##           CP nsplit rel error  xerror    xstd
## 1  0.1460795      0   1.00000 1.00897 0.16867
## 2  0.0841328      1   0.85392 0.95025 0.16884
## 3  0.0580047      2   0.76979 0.89084 0.16834
## 4  0.0572352      3   0.71178 0.85898 0.16815
## 5  0.0555879      4   0.65455 0.84634 0.16843
## 6  0.0182684      5   0.59896 0.70167 0.13785
## 7  0.0136391      7   0.56242 0.65307 0.13270
## 8  0.0106498     10   0.52151 0.63299 0.12931
## 9  0.0096720     11   0.51086 0.63316 0.12778
## 10 0.0090067     12   0.50118 0.61413 0.12113
## 11 0.0077965     14   0.48317 0.60627 0.12114
## 12 0.0064596     15   0.47537 0.60484 0.11897
## 13 0.0062628     16   0.46891 0.60523 0.11895
## 14 0.0028757     18   0.45639 0.59252 0.11873
## 15 0.0027046     19   0.45351 0.58486 0.10956
## 16 0.0014373     20   0.45081 0.58536 0.10935
## 17 0.0010135     21   0.44937 0.57882 0.10899
## 18 0.0010000     22   0.44836 0.57769 0.10896