O projeto possui o foco de criar um modelo de aprendizado de máquina para prever se um cliente pode ou não cancelar seu plano e a probabilidade disso ocorrer. Para desenvolver o estudo utilizaremos a linguagem R.
Churn é uma métrica que indica o número de clientes que reincidem seus contratos em determinado período. Para calcular o churn, o que você precisa fazer é somar o número de clientes que cancelou seu produto/serviço no período analisado.
Para que uma empresa consiga fazer a expansão da sua base de clientes, é preciso que o número de novos clientes exceda o seu churn rate – a taxa de clientes cancelados.
Fonte: (https://resultadosdigitais.com.br/blog/o-que-e-churn/); acessado em 11 de abril de 2021.
Análise preditiva é uma técnica analítica avançada que usa dados, algoritmos e machine learning para antecipar tendências e fazer projeções nos negócios. Graças ao avanço computacional, já é possível analisar grandes volumes de dados (Big Data) para encontrar padrões e avaliar as futuras possibilidades a partir do histórico da empresa.
Autor: Patel, Neil; (https://neilpatel.com/br/blog/analise-preditiva/); acessado em 11 de abril de 2021.
Autor: Alcantara, Joyce; (https://e-millennium.com.br/o-que-e-analise-preditiva-e-como-ela-agrega-valor-para-os-negocios/); acessado em 11 de abril de 2021.
O banco de dados foi extraído do site Kaggle e vamos importá-lo para software.
churn <- read.csv('/Users/giulianafarabolini/Downloads/WA_Fn-UseC_-Telco-Customer-Churn.csv')
customerID: 7043 ID’s únicos, cada um representando um cliente;
gender: gênero do cliente;
SeniorCitizen: se o cliente é idoso ou não;
Partner: se cliente tem um cônjuge ou não;
Dependents: se o cliente possui dependentes ou não;
tenure: número de meses que o cliente está/ficou na companhia;
PhoneService: se o cliente possui serviço de telefone ou não;
MultipleLines: se o cliente possui múltiplas linhas de telefone;
InternetService: tipo de serviço de internet;
OnlineSecurity: se o cliente possui algum tipo de proteção online;
OnlineBackup: se o cliente tem backup online;
DeviceProtection: se o cliente possui algum tipo de proteção para celular;
TechSupport: se o cliente tem suporte técnico;
StreamingTV: se o cliente tem serviço de streaming de TV (TV por assinatura);
StreamingMovies: se o cliente tem algum tipo de serviço de streaming para filmes (Netflix, Telecine, Amazon Prime, etc…);
Contract: duração do contrato;
PaperlessBilling: se o cliente recebe a fatura virtualmente (sem a impressão de papel);
PaymentMethod: método de pagamento utilizado;
MonthlyCharges: valor da fatura mensal do cliente;
TotalCharges: valor total já pago pelo cliente;
Churn: variável indicando se o cliente rescindiu ou não seu contrato com a companhia no último mês.
Vamos analisar o banco de dados e suas características:
str(churn)
## 'data.frame': 7043 obs. of 21 variables:
## $ customerID : chr "7590-VHVEG" "5575-GNVDE" "3668-QPYBK" "7795-CFOCW" ...
## $ gender : chr "Female" "Male" "Male" "Male" ...
## $ SeniorCitizen : int 0 0 0 0 0 0 0 0 0 0 ...
## $ Partner : chr "Yes" "No" "No" "No" ...
## $ Dependents : chr "No" "No" "No" "No" ...
## $ tenure : int 1 34 2 45 2 8 22 10 28 62 ...
## $ PhoneService : chr "No" "Yes" "Yes" "No" ...
## $ MultipleLines : chr "No phone service" "No" "No" "No phone service" ...
## $ InternetService : chr "DSL" "DSL" "DSL" "DSL" ...
## $ OnlineSecurity : chr "No" "Yes" "Yes" "Yes" ...
## $ OnlineBackup : chr "Yes" "No" "Yes" "No" ...
## $ DeviceProtection: chr "No" "Yes" "No" "Yes" ...
## $ TechSupport : chr "No" "No" "No" "Yes" ...
## $ StreamingTV : chr "No" "No" "No" "No" ...
## $ StreamingMovies : chr "No" "No" "No" "No" ...
## $ Contract : chr "Month-to-month" "One year" "Month-to-month" "One year" ...
## $ PaperlessBilling: chr "Yes" "No" "Yes" "No" ...
## $ PaymentMethod : chr "Electronic check" "Mailed check" "Mailed check" "Bank transfer (automatic)" ...
## $ MonthlyCharges : num 29.9 57 53.9 42.3 70.7 ...
## $ TotalCharges : num 29.9 1889.5 108.2 1840.8 151.7 ...
## $ Churn : chr "No" "No" "Yes" "No" ...
A tabela é composta por 7.043 observações(linhas) e 21 variáveis (colunas).
A maioria das variáveis entrou como classificação caracter, porém a maior parte da base é factor, por isso vamos alterar suas classes:
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
churn <- churn %>%
mutate_at(vars(2:5,7:18,21), as.factor)
As colunas 10 a 15 tem observações que são diferentes, mas possuem o mesmo significado. Para facilitar a análise iremos substitui-las e unificar as observações.
churn[churn == 'No internet service'] <- 'No'
O mesmo acontece com a coluna MultipleLines, vamos repetir a ação anterior, porém com a observação “No phone service”.
levels(churn$MultipleLines)
## [1] "No" "No phone service" "Yes"
levels(churn$MultipleLines) <- c('No', 'No', 'Yes')
Já a coluna SeniorCitizen traz a informação ‘1’ para quem é idoso e ‘0’ para o contrário, como feito anteriormente vamos substituir esses valores para ‘Yes’ e ‘No’, respectivamente para uniformizar nossa base.
levels(churn$SeniorCitizen)
## [1] "0" "1"
levels(churn$SeniorCitizen) <- c('No', 'Yes')
Verificando se a base possui valores nulos
sum(is.na(churn))
## [1] 11
Temos valores nulos, agora vamos identificar em qual coluna estão:
sapply(churn, function(x) sum(is.na(x)))
## customerID gender SeniorCitizen Partner
## 0 0 0 0
## Dependents tenure PhoneService MultipleLines
## 0 0 0 0
## InternetService OnlineSecurity OnlineBackup DeviceProtection
## 0 0 0 0
## TechSupport StreamingTV StreamingMovies Contract
## 0 0 0 0
## PaperlessBilling PaymentMethod MonthlyCharges TotalCharges
## 0 0 0 11
## Churn
## 0
Observamos que todos os valores nulos estão na coluna TotalCharges, mas como o dataset nos traz os valores pagos no mês e tempo do contrato, podemos criar os valores nulos se houver necessidade.
churn[is.na(churn$TotalCharges),1:6]
## customerID gender SeniorCitizen Partner Dependents tenure
## 489 4472-LVYGI Female No Yes Yes 0
## 754 3115-CZMZD Male No No Yes 0
## 937 5709-LVOEQ Female No Yes Yes 0
## 1083 4367-NUYAO Male No Yes Yes 0
## 1341 1371-DWPAZ Female No Yes Yes 0
## 3332 7644-OMVMY Male No Yes Yes 0
## 3827 3213-VVOLG Male No Yes Yes 0
## 4381 2520-SGTTA Female No Yes Yes 0
## 5219 2923-ARZLG Male No Yes Yes 0
## 6671 4075-WKNIU Female No Yes Yes 0
## 6755 2775-SEFEE Male No No Yes 0
Vemos que os clientes que estão com valores nulos é porque possuem o tempo de contrato zero, ou seja, não realizaram o primeiro pagamento. Será que temos outros clientes com o tempo de contrato zerado?
library(dplyr)
churn %>%
filter(tenure == 0) %>%
summarize("Zero Tenure" = n())
## Zero Tenure
## 1 11
Esses são os únicos clientes, então vamos removê-los do dataset, já que não possuem um tempo considerável de contrato para influenciar as nossas análises, além do que essas observações representam apenas 0,15% do banco de dados total.
nondchurn <- churn[complete.cases(churn), ]
dim(nondchurn)
## [1] 7032 21
Não precisaremos da variável customerID para gráficos ou modelagem, portanto, ela pode ser removida.
nvdatasetchurn <- nondchurn %>%
select(-customerID)
O conjunto de dados nos traz que 26,58% dos clientes reincidiram os contratos.
library(ggplot2)
nvdatasetchurn %>%
group_by(Churn) %>%
summarise(Number = n()) %>%
mutate(Percentual = prop.table(Number)*100) %>%
ggplot(aes(Churn, Percentual)) +
geom_col(aes(fill = Churn)) +
labs(title = "Taxa Churn") +
theme(plot.title = element_text(hjust = 0.5)) +
geom_text(aes(label = sprintf("%.2f%%", Percentual)), hjust = 0.5,vjust =1, size = 4) +
theme_minimal()
## `summarise()` ungrouping output (override with `.groups` argument)
Antes de começarmos a fazer nossa modelagem, vamos analisar os perfis dos clientes.
library(cowplot)
##
## ********************************************************
## Note: As of version 1.0.0, cowplot does not change the
## default ggplot2 theme anymore. To recover the previous
## behavior, execute:
## theme_set(theme_cowplot())
## ********************************************************
library(stringr)
plot_grid(ggplot(nvdatasetchurn, aes(x = gender, fill = Churn)) +
geom_bar() +
geom_text(aes(y = ..count.. -200,
label = paste0(round(prop.table(..count..),4) * 100, '%')),
stat = 'count', position = position_dodge(.1), size = 3)+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = SeniorCitizen, fill = Churn)) +
geom_bar() +
geom_text(aes(y = ..count.. -200,
label = paste0(round(prop.table(..count..),4) * 100, '%')),
stat = 'count', position = position_dodge(.1), size = 3)+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = Partner, fill = Churn)) +
geom_bar() +
geom_text(aes(y = ..count.. -200,
label = paste0(round(prop.table(..count..),4) * 100, '%')),
stat = 'count', position = position_dodge(.1), size = 3)+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = Dependents, fill = Churn)) +
geom_bar() +
geom_text(aes(y = ..count.. -200,
label = paste0(round(prop.table(..count..),4) * 100, '%')),
stat = 'count', position = position_dodge(.1), size = 3)+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()))+
labs(title = "Perfil do Cliente") +
theme(plot.title = element_text(hjust = 0.5))
A partir dos gráficos podemos ver que a mostra é dívida uniformemente por gênero e status de parceiro. Uma minoria da amostra é idosa e a grande maioria não possui dependentes. Conseguimos perceber que o perfil dos clientes não churn e dos clientes churn diferem apenas na variável partner, onde os clientes que reincidiram o contrato a maioria não possui companheiro e já os clientes não churn a maioria possui companheiros.
ggplot(nvdatasetchurn, aes(x = tenure, fill = Churn)) +
geom_bar() +
labs(x = "Tenure") +
theme_minimal()
A maioria dos clientes churn, cancelam seus contratos antes de completar 2 meses, como podemos observar no gráfico acima.
plot_grid(ggplot(nvdatasetchurn, aes(x = Contract, fill = Churn)) +
geom_bar() +
geom_text(aes(y = ..count.. -200,
label = paste0(round(prop.table(..count..),4) * 100, '%')),
stat = 'count', position = position_dodge(.1), size = 3)+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = PaymentMethod, fill = Churn)) +
geom_bar() +
geom_text(aes(y = ..count.. -200,
label = paste0(round(prop.table(..count..),4) * 100, '%')),
stat = 'count', position = position_dodge(.1), size = 3)+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = InternetService, fill = Churn)) +
geom_bar() +
geom_text(aes(y = ..count.. -200,
label = paste0(round(prop.table(..count..),4) * 100, '%')),
stat = 'count', position = position_dodge(.1), size = 3)+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()))
A maior parte dos clientes churn preferem o meio de pagamento Eletronic Check e possuem contratos mensais. Também preferem pelo serviço de internet Fiber optic.
Vamos dar uma olhada nas demais variáveis:
plot_grid(ggplot(nvdatasetchurn, aes(x = PhoneService, fill = Churn)) +
geom_bar()+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = MultipleLines, fill = Churn)) +
geom_bar()+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = OnlineBackup, fill = Churn)) +
geom_bar()+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = DeviceProtection, fill = Churn)) +
geom_bar()+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = TechSupport, fill = Churn)) +
geom_bar()+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = StreamingTV, fill = Churn)) +
geom_bar()+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = StreamingTV, fill = Churn)) +
geom_bar()+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()),
ggplot(nvdatasetchurn, aes(x = StreamingMovies, fill = Churn)) +
geom_bar()+
theme_minimal()+
theme(axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title.y = element_blank()))+
labs(title = "Demais Serviços") +
theme(plot.title = element_text(hjust = 0.5))
Neste projeto, usarei 3 modelos de aprendizado de máquina (Naive Bayes, Decision Tree e Random Forest). Para começar a realizar os modelos, vamos primeiramente dividir a base de dados em dois. O subconjunto de treinamento será aproximadamente 70% da amostra original, com o restante sendo o subconjunto de teste.
library(caret)
## Loading required package: lattice
set.seed(56)
split_train_test <- createDataPartition(nvdatasetchurn$Churn,p=0.7,list=FALSE)
dtrain<- nvdatasetchurn[split_train_test,]
dtest<- nvdatasetchurn[-split_train_test,]
A análise da árvore de decisão é um método de classificação que usa modelos de decisões em forma de árvore e seus possíveis resultados. Este método exploratório identificará as variáveis mais importantes relacionadas ao churn em um formato hierárquico.
library(rpart)
library(rpart.plot)
tr_fit <- rpart(Churn ~., data = dtrain, method="class")
rpart.plot(tr_fit)
A variável mais importante é Contract. Com o modelo podemos ver que os clientes mais engajados preferem contratos anuais, serviço de internet DSL e permanecem na empresa mais de 15 meses.
Para avaliar a precisão da nossa árvore de decisão vamos utilizar a matriz de confusão:
tr_prob1 <- predict(tr_fit, dtest)
tr_pred1 <- ifelse(tr_prob1[,2] > 0.5,"Yes","No")
table(Predicted = tr_pred1, Actual = dtest$Churn)
## Actual
## Predicted No Yes
## No 1406 284
## Yes 142 276
A partir dessa matriz de confusão, podemos ver que o modelo tem um bom desempenho na previsão de clientes não rotativos, mas não tem um desempenho tão bom na previsão de clientes rotativos.
E quanto a acuracidade do modelo?
tr_prob2 <- predict(tr_fit, dtrain)
tr_pred2 <- ifelse(tr_prob2[,2] > 0.5,"Yes","No")
tr_tab1 <- table(Predicted = tr_pred2, Actual = dtrain$Churn)
tr_tab2 <- table(Predicted = tr_pred1, Actual = dtest$Churn)
# Treinamento
library(caret)
confusionMatrix(
as.factor(tr_pred2),
as.factor(dtrain$Churn),
positive = "Yes"
)
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 3272 674
## Yes 343 635
##
## Accuracy : 0.7935
## 95% CI : (0.7819, 0.8047)
## No Information Rate : 0.7342
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.4245
##
## Mcnemar's Test P-Value : < 2.2e-16
##
## Sensitivity : 0.4851
## Specificity : 0.9051
## Pos Pred Value : 0.6493
## Neg Pred Value : 0.8292
## Prevalence : 0.2658
## Detection Rate : 0.1290
## Detection Prevalence : 0.1986
## Balanced Accuracy : 0.6951
##
## 'Positive' Class : Yes
##
# Teste
confusionMatrix(
as.factor(tr_pred1),
as.factor(dtest$Churn),
positive = "Yes"
)
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 1406 284
## Yes 142 276
##
## Accuracy : 0.7979
## 95% CI : (0.7801, 0.8149)
## No Information Rate : 0.7343
## P-Value [Acc > NIR] : 6.398e-12
##
## Kappa : 0.4364
##
## Mcnemar's Test P-Value : 8.405e-12
##
## Sensitivity : 0.4929
## Specificity : 0.9083
## Pos Pred Value : 0.6603
## Neg Pred Value : 0.8320
## Prevalence : 0.2657
## Detection Rate : 0.1309
## Detection Prevalence : 0.1983
## Balanced Accuracy : 0.7006
##
## 'Positive' Class : Yes
##
tr_acc <- sum(diag(tr_tab2))/sum(tr_tab2)
tr_acc
## [1] 0.7979127
O modelo de árvore de decisão é bastante preciso, prevendo corretamente o status de rotatividade de clientes no subconjunto de teste 79% do tempo.
A análise floresta aleatória é outro método de classificação de aprendizado de máquina freqüentemente usado na análise de rotatividade de clientes. O método opera construindo múltiplas árvores de decisão e construindo modelos com base em estatísticas resumidas dessas árvores de decisão.
Começaremos identificando o número de variáveis amostradas aleatoriamente. No pacote randomForest, isso é referido como o parâmetro ou argumento ‘mtry’.
#Definir parâmetros de controle para seleção aleatória de modelo de floresta
ctrl <- trainControl(method = "cv", number=5,
classProbs = TRUE, summaryFunction = twoClassSummary)
#Seleção exploratória de modelo de floresta aleatória
rf_fit1 <- train(Churn ~., data = dtrain,
method = "rf",
ntree = 75,
tuneLength = 5,
metric = "ROC",
trControl = ctrl)
rf_fit1
## Random Forest
##
## 4924 samples
## 19 predictor
## 2 classes: 'No', 'Yes'
##
## No pre-processing
## Resampling: Cross-Validated (5 fold)
## Summary of sample sizes: 3940, 3939, 3939, 3939, 3939
## Resampling results across tuning parameters:
##
## mtry ROC Sens Spec
## 2 0.8332208 0.9629322 0.3009798
## 8 0.8239186 0.8993084 0.4858764
## 15 0.8165053 0.8901798 0.4881665
## 22 0.8165443 0.8912863 0.4881665
## 29 0.8101833 0.8835408 0.4904478
##
## ROC was used to select the optimal model using the largest value.
## The final value used for the model was mtry = 2.
saveRDS(rf_fit1, "Churn.RDS")
rf_fit1 <- readRDS("Churn.RDS")
O modelo descobriu que o valor ideal para mtry é 2. A partir deste modelo, podemos investigar a importância relativa das variáveis preditoras de rotatividade.
library(randomForest)
## randomForest 4.6-14
## Type rfNews() to see new features/changes/bug fixes.
##
## Attaching package: 'randomForest'
## The following object is masked from 'package:ggplot2':
##
## margin
## The following object is masked from 'package:dplyr':
##
## combine
rf_fit2 <- randomForest(Churn ~., data = dtrain,
ntree = 75, mtry = 2,
importance = TRUE, proximity = TRUE)
# Mostra a importância da variável da árvore aleatória
varImpPlot(rf_fit2, sort=T, n.var = 10,
main = 'Top 10 variáveis importantes')
Semelhante à árvore de decisão, este modelo de floresta aleatório identificou tenure e Contract como preditores importantes para a rotatividade. Em contrapartida Internet Service não está entre os top 3 e é substituída por TotalCharges.
Vamos examinar o desempenho deste modelo de floresta aleatório. Começaremos com a matriz de confusão.
rf_pred1 <- predict(rf_fit2, dtest)
table(Predicted = rf_pred1, Actual = dtest$Churn)
## Actual
## Predicted No Yes
## No 1433 289
## Yes 115 271
plot(rf_fit2)
O desempenho é um tanto semelhante ao modelo de árvore de decisão. A taxa de falsos negativos é baixa, mas a taxa de falsos positivos é bastante alta. E quanto à precisão geral?
rf_pred2 <- predict(rf_fit2, dtrain)
rf_tab1 <- table(Predicted = rf_pred2, Actual = dtrain$Churn)
rf_tab2 <- table(Predicted = rf_pred1, Actual = dtest$Churn)
# Treinamento
confusionMatrix(
as.factor(rf_pred2),
as.factor(dtrain$Churn),
positive = "Yes"
)
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 3466 445
## Yes 149 864
##
## Accuracy : 0.8794
## 95% CI : (0.8699, 0.8883)
## No Information Rate : 0.7342
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.6669
##
## Mcnemar's Test P-Value : < 2.2e-16
##
## Sensitivity : 0.6600
## Specificity : 0.9588
## Pos Pred Value : 0.8529
## Neg Pred Value : 0.8862
## Prevalence : 0.2658
## Detection Rate : 0.1755
## Detection Prevalence : 0.2057
## Balanced Accuracy : 0.8094
##
## 'Positive' Class : Yes
##
# Teste
confusionMatrix(
as.factor(rf_pred1),
as.factor(dtest$Churn),
positive = "Yes"
)
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 1433 289
## Yes 115 271
##
## Accuracy : 0.8083
## 95% CI : (0.7909, 0.825)
## No Information Rate : 0.7343
## P-Value [Acc > NIR] : 1.088e-15
##
## Kappa : 0.4547
##
## Mcnemar's Test P-Value : < 2.2e-16
##
## Sensitivity : 0.4839
## Specificity : 0.9257
## Pos Pred Value : 0.7021
## Neg Pred Value : 0.8322
## Prevalence : 0.2657
## Detection Rate : 0.1286
## Detection Prevalence : 0.1831
## Balanced Accuracy : 0.7048
##
## 'Positive' Class : Yes
##
rf_acc <- sum(diag(rf_tab2))/sum(rf_tab2)
rf_acc
## [1] 0.8083491
O modelo de floresta aleatória é um pouco mais preciso do que o modelo de árvore de decisão, sendo capaz de prever corretamente o status de rotatividade de um cliente no subconjunto de teste com 80% de precisão.
A regressão logística envolve a regressão de variáveis preditoras em um resultado binário usando uma função de ligação binomial. Vamos ajustar o modelo usando a função de modelagem linear geral básica em R, ‘glm’.
lr_fit <- glm(Churn ~., data = dtrain,
family=binomial(link='logit'))
summary(lr_fit)
##
## Call:
## glm(formula = Churn ~ ., family = binomial(link = "logit"), data = dtrain)
##
## Deviance Residuals:
## Min 1Q Median 3Q Max
## -1.8966 -0.6818 -0.2767 0.7457 3.2597
##
## Coefficients:
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 1.232e+00 9.637e-01 1.278 0.201258
## genderMale -1.242e-02 7.748e-02 -0.160 0.872611
## SeniorCitizenYes 2.358e-01 1.001e-01 2.355 0.018532 *
## PartnerYes -7.698e-02 9.255e-02 -0.832 0.405513
## DependentsYes -1.896e-01 1.071e-01 -1.770 0.076691 .
## tenure -6.960e-02 7.728e-03 -9.006 < 2e-16 ***
## PhoneServiceYes -1.368e-01 7.678e-01 -0.178 0.858579
## MultipleLinesYes 3.824e-01 2.088e-01 1.832 0.067023 .
## InternetServiceFiber optic 1.316e+00 9.422e-01 1.396 0.162633
## InternetServiceNo -1.700e+00 9.569e-01 -1.777 0.075603 .
## OnlineSecurityYes -2.218e-01 2.102e-01 -1.055 0.291466
## OnlineBackupYes -1.189e-01 2.074e-01 -0.573 0.566495
## DeviceProtectionYes 3.831e-02 2.080e-01 0.184 0.853874
## TechSupportYes -3.033e-01 2.136e-01 -1.420 0.155750
## StreamingTVYes 4.271e-01 3.854e-01 1.108 0.267716
## StreamingMoviesYes 4.323e-01 3.863e-01 1.119 0.263076
## ContractOne year -6.228e-01 1.293e-01 -4.816 1.46e-06 ***
## ContractTwo year -1.389e+00 2.145e-01 -6.473 9.63e-11 ***
## PaperlessBillingYes 3.205e-01 8.915e-02 3.595 0.000324 ***
## PaymentMethodCredit card (automatic) -2.102e-01 1.355e-01 -1.551 0.120894
## PaymentMethodElectronic check 2.365e-01 1.125e-01 2.103 0.035466 *
## PaymentMethodMailed check -2.345e-01 1.384e-01 -1.694 0.090308 .
## MonthlyCharges -2.912e-02 3.752e-02 -0.776 0.437718
## TotalCharges 4.636e-04 8.674e-05 5.344 9.08e-08 ***
## ---
## Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
##
## (Dispersion parameter for binomial family taken to be 1)
##
## Null deviance: 5702.8 on 4923 degrees of freedom
## Residual deviance: 4074.2 on 4900 degrees of freedom
## AIC: 4122.2
##
## Number of Fisher Scoring iterations: 6
As variáveis tenure, Contract e TotalCharges têm os valores p mais baixos e podem ser identificados como os melhores preditores de rotatividade de clientes.
Vamos examinar a matriz de confusão com base em nosso modelo de regressão logística.
lr_prob1 <- predict(lr_fit, dtest, type="response")
lr_pred1 <- ifelse(lr_prob1 > 0.5,"Yes","No")
table(Predicted = lr_pred1, Actual = dtest$Churn)
## Actual
## Predicted No Yes
## No 1403 247
## Yes 145 313
Semelhante aos outros modelos, a taxa de falsos negativos é baixa, porém não tão baixa. Em contrapartida, a taxa de falsos positivos está na verdade acima de 50%, portanto, tem um desempenho melhor do que os algoritmos de aprendizado de máquina anteriores.
A acuracidade pode ser obtida de forma semelhante aos modelos anteriores.
lr_prob2 <- predict(lr_fit, dtrain, type="response")
lr_pred2 <- ifelse(lr_prob2 > 0.5,"Yes","No")
lr_tab1 <- table(Predicted = lr_pred2, Actual = dtrain$Churn)
lr_tab2 <- table(Predicted = lr_pred1, Actual = dtest$Churn)
# Treinamento
confusionMatrix(
as.factor(lr_pred2),
as.factor(dtrain$Churn),
positive = "Yes"
)
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 3227 595
## Yes 388 714
##
## Accuracy : 0.8004
## 95% CI : (0.7889, 0.8115)
## No Information Rate : 0.7342
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.4614
##
## Mcnemar's Test P-Value : 5.019e-11
##
## Sensitivity : 0.5455
## Specificity : 0.8927
## Pos Pred Value : 0.6479
## Neg Pred Value : 0.8443
## Prevalence : 0.2658
## Detection Rate : 0.1450
## Detection Prevalence : 0.2238
## Balanced Accuracy : 0.7191
##
## 'Positive' Class : Yes
##
# Teste
confusionMatrix(
as.factor(lr_pred1),
as.factor(dtest$Churn),
positive = "Yes"
)
## Confusion Matrix and Statistics
##
## Reference
## Prediction No Yes
## No 1403 247
## Yes 145 313
##
## Accuracy : 0.814
## 95% CI : (0.7968, 0.8304)
## No Information Rate : 0.7343
## P-Value [Acc > NIR] : < 2.2e-16
##
## Kappa : 0.494
##
## Mcnemar's Test P-Value : 3.374e-07
##
## Sensitivity : 0.5589
## Specificity : 0.9063
## Pos Pred Value : 0.6834
## Neg Pred Value : 0.8503
## Prevalence : 0.2657
## Detection Rate : 0.1485
## Detection Prevalence : 0.2173
## Balanced Accuracy : 0.7326
##
## 'Positive' Class : Yes
##
lr_acc <- sum(diag(lr_tab2))/sum(lr_tab2)
lr_acc
## [1] 0.8140417
A taxa de precisão de 81,4% do modelo de regressão logística supera ligeiramente os modelos de árvore de decisão e floresta aleatória.
Depois de passar por várias etapas preparatórias, incluindo carregamento de dados/biblioteca e pré-processamento, realizamos três métodos de classificação estatística comuns na análise de churn. Identificamos várias variáveis preditoras de rotatividade importantes desses modelos e comparamos esses modelos em medidas de precisão.
Em suma: