Introdução

A maioria dos “modeleiros”, Estatísticos, Analistas de Dados, “Machine Learners” concordam que a parte que toma a maior parte do tempo no desenvolvimento de um modelo é a limpeza e a preparação da base de dados. Uns dizem que isso toma 80% do tempo do trabalho.

Por isso, vamos aqui pegar uma base de dados não tratada e preparar e limpar essa base para um algoritmo de Machine Learning. Vamos acessar os dados de rotatividade de clientes de uma empresa. Nossa variável alvo é a que chamamos de “Saiu”, que significa se o cliente deixou ou não a empresa.

Acessando os dados

library(tidyverse)
dados <- read.csv("Churn.csv", sep = ';', na.strings = "",stringsAsFactors = TRUE)
head(dados)
##   X0  X1 X2        X3 X4 X4.1       X6 X7 X8 X9      X10 X11
## 1  1 619 RS  Feminino 42    2        0  1  1  1 10134888   1
## 2  2 608 SC  Feminino 41    1  8380786  1  0  1 11254258   0
## 3  3 502 RS  Feminino 42    8  1596608  3  1  0 11393157   1
## 4  4 699 RS  Feminino 39    1        0  2  0  0  9382663   0
## 5  5 850 SC  Feminino 43    2 12551082  1  1  1   790841   0
## 6  6 645 SC Masculino 44    8 11375578  2  1  0 14975671   1

Renomear as colunas

Como as colunas não tinham nome, vamos colocar os nomes das colunas de acordo com o conhecimento adquirido, por exemplo com o analista de negócio da empresa.

#Dar nomes corretos as colunas
colnames(dados) <- c("Id","Score","Estado","Genero","Idade","Patrimonio","Saldo","Produtos","TemCartCredito","Ativo","Salario","Saiu")
head(dados)
##   Id Score Estado    Genero Idade Patrimonio    Saldo Produtos TemCartCredito
## 1  1   619     RS  Feminino    42          2        0        1              1
## 2  2   608     SC  Feminino    41          1  8380786        1              0
## 3  3   502     RS  Feminino    42          8  1596608        3              1
## 4  4   699     RS  Feminino    39          1        0        2              0
## 5  5   850     SC  Feminino    43          2 12551082        1              1
## 6  6   645     SC Masculino    44          8 11375578        2              1
##   Ativo  Salario Saiu
## 1     1 10134888    1
## 2     1 11254258    0
## 3     0 11393157    1
## 4     0  9382663    0
## 5     1   790841    0
## 6     0 14975671    1

Agora sim conseguimos entender os dados e podemos fazer as primeiras análises

Vamos explorar as colunas categóricas.

Vamos criar as variáveis para os gráficos

#Estado

count_estado <- dados %>% count(Estado)

ggplot(count_estado, aes(x=Estado, y=n)) + geom_bar(stat='identity', aes(fill=Estado))+ theme_bw() +
  ggtitle('Estados')

Parece que há um erro na variável, vamos corrigir isso. Só há dados do sul.Logo não há os Estados RP,SP e TD. Vamos substitutir todos para RS

dados<-dados %>% mutate(Estado= case_when(
  Estado=='RP' ~ 'RS',
  Estado=='TD' ~ 'RS',
  Estado=='RS' ~ 'RS',
  Estado=='SP' ~ 'RS',
   Estado=='SC' ~ 'SC',
   Estado=='PR' ~ 'PR',
))

Agora vamos refazer o graficos

#Estado

count_estado <- dados %>% count(Estado)

ggplot(count_estado, aes(x=Estado, y=n)) + geom_bar(stat='identity', aes(fill=Estado))+ theme_bw() +
  ggtitle('Estados')

Agora sim a variavel Estado esta correta

Variavel Genero

#Genero
count_genero<- dados %>% count(Genero)
count_genero
##      Genero   n
## 1         F   2
## 2       Fem   1
## 3  Feminino 461
## 4         M   6
## 5 Masculino 521
## 6      <NA>   8

Parece que a variavel genero também tem problemas. Vamos corrir e deixar somente as categorias Masculino e Feminino.

dados<-dados %>% mutate(Genero= case_when(
  Genero ==  'Fem'~ 'Feminino',
  Genero ==  'F'~ 'Feminino',
  Genero ==  'Feminino'~ 'Feminino',
  Genero ==  'M'~ 'Masculino',
  Genero ==  'Masculino'~ 'Masculino'
  
  
  
))

Vamos ver se há valores faltantes(NA)

sum(is.na(dados$Genero))
## [1] 8

Há 8 nulos, vamos transformar na classe majoritaria. Poderiamos também excluir esses valores.

dados$Genero<- ifelse(is.na(dados$Genero), 'Masculino', dados$Genero)
sum(is.na(dados$Genero))
## [1] 0

Agora o gráfico:

count_genero<- dados %>% count(Genero)

ggplot(count_genero, aes(x=Genero, y=n)) + geom_bar(stat = 'identity', aes(fill=Genero))+
  ggtitle('Genero') + theme_bw()

Agora vamos verificar a variavel alvo:

count_target<-dados %>% count(Saiu)
 count_target$Saiu <- as.factor(count_target$Saiu)

ggplot(count_target, aes(x= Saiu, y=n)) + geom_bar(stat='identity', aes(fill=Saiu))+ theme_bw()+
   ggtitle('Target')

Base um pouco desbalanceada, talvez fosse interessante fazer um oversampling. Porém o objetivo aqui é só explorar os dados e coriigir dados faltantes e duplicados.

Vamos agora olhar a variavel categorica x a variavel alvo:

round(prop.table(table(dados$Estado,dados$Saiu, dnn = c('Estados','Alvo')))*100,2)
##        Alvo
## Estados     0     1
##      PR 18.22  7.51
##      RS 40.74  7.71
##      SC 20.72  5.11
round(prop.table(table(dados$Genero,dados$Saiu, dnn = c('Genero','Alvo')))*100,2)
##            Alvo
## Genero          0     1
##   Feminino  34.43 12.01
##   Masculino 45.25  8.31

Agora as as variáveis numéricas:

ggplot(dados, aes(x=Score)) + geom_histogram(aes(y=..density..),color="black", fill="white")+
  geom_density(alpha=.2, fill="#FF6666") + theme_bw() + ggtitle('Score')
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Parece não ter problemas nos dados de score. Seria interessante saber se tem sentido os valores máximos e minimos de score apontados no histograma. Podemos visualizar também o boxplot:

ggplot(dados, aes( x=1, y=Score)) + geom_boxplot(color="black", fill="red") + theme_bw()+
  ggtitle('Score')

Score versus variavel alvo:

dados$Saiu <- as.factor(dados$Saiu)

ggplot(dados, aes(x=Saiu, y=Score,fill=Saiu)) + geom_boxplot() + theme_bw()+
  ggtitle("Score x Target")

Variavel Idade

ggplot(dados, aes(x=Idade))+ geom_histogram(aes(y=..density..),color="black", fill="white")+ 
    geom_density(alpha=.2, fill='#99d8c9')
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Os dados parecem ter distribuição aproxiamdamente normal. Porém parece ter idades negativas. Vamos olhar o boxplot desses dados:

ggplot(dados, aes(x=1, y=Idade)) + geom_boxplot()+ theme_bw()+
  ggtitle('Idade')

Veja que há idades negativas e idades com mais de 130 anos.

Esses dados devem ser corrigidos, uma forma é susbtitutir esses valores pela idade mediana. Antes vejamos se tem NAS:

sum(is.na(dados$Idade))
## [1] 0
sum(dados$Idade<0)
## [1] 2

Não há NAS mas há 2 negativos, agora sim vamos ajustar os dados:

dados <- dados %>% mutate(Idade=ifelse(Idade<0 | Idade>120,median(Idade, na.rm = TRUE),Idade))

Refazendo o grafico:

ggplot(dados, aes(x=1, y=Idade)) + geom_boxplot()+ theme_bw()+
  ggtitle('Idade')

Agora sim, não há mais outliers na variável. Podemos ver o boxplot da idade x target

ggplot(dados, aes(x=Saiu, y=Idade,fill=Saiu)) + geom_boxplot()+ theme_bw()+
  ggtitle('Idade')

Variável patrimonio:

sum(is.na(dados$Patrimonio))
## [1] 0

Perceba que não há NA

Olhando o máximo e mínimo parece que os dados estão ok.

max(dados$Patrimonio)
## [1] 10
min(dados$Patrimonio)
## [1] 0

Variável Saldo

Parece não ter problema também:

sum(is.na(dados$Saldo))
## [1] 0
max(dados$Saldo)
## [1] 21177431
min(dados$Saldo)
## [1] 0

Variável Produtos

Parece não ter problema também:

sum(is.na(dados$Produtos))
## [1] 0
max(dados$Produtos)
## [1] 4
min(dados$Produtos)
## [1] 1

Variável TemCartCredito

Parece não ter problema também:

count_card <- dados %>% count(TemCartCredito)
count_card
##   TemCartCredito   n
## 1              0 297
## 2              1 702

Contra a variavel alvo

count_card_targer <- dados %>% count(TemCartCredito, Saiu)
count_card_targer
##   TemCartCredito Saiu   n
## 1              0    0 237
## 2              0    1  60
## 3              1    0 559
## 4              1    1 143

Vamos analisar graficamente:

dados<- dados %>% mutate(tem_cartao=ifelse(TemCartCredito==1,'sim', 'não'))

count_card_targer <- dados %>% count(tem_cartao, Saiu)
count_card_targer
##   tem_cartao Saiu   n
## 1        não    0 237
## 2        não    1  60
## 3        sim    0 559
## 4        sim    1 143
ggplot(count_card_targer, aes(x=Saiu, y=n)) + geom_bar(stat = 'identity', aes(fill=tem_cartao))+
  theme_bw()+ ggtitle('Cartão x saiu')

Cartão Ativo ou Não. Vamos só ver se há algum numero distinjto de 0 ou 1. caso Contrário estará ok,

unique(dados$Ativo)
## [1] 1 0

Logo está ok

Agora a variável salario.

Tirando NAS:

#tratar salários
summary(dados$Salario)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max.      NA's 
## 9.677e+03 3.029e+06 8.703e+06 3.529e+07 1.405e+07 1.193e+10         7
#ver mediana
median(dados$Salario,na.rm = T)
## [1] 8703250
#atribuir mediana a NAs
dados[is.na(dados$Salario),]$Salario = median(dados$Salario,na.rm = T)
#buscar NAS em salario para checar
dados[!complete.cases(dados$Salario),]
##  [1] Id             Score          Estado         Genero         Idade         
##  [6] Patrimonio     Saldo          Produtos       TemCartCredito Ativo         
## [11] Salario        Saiu           tem_cartao    
## <0 rows> (or 0-length row.names)

Plotando o grafico:

ggplot(dados, aes(x=1, y=Salario))+ geom_boxplot()

Veja que há outliers, criando um parametro com desvio padrão para eliminar os outliers:

desv = sd(dados$Salario, na.rm = T)
desv
## [1] 528720617
dados[dados$Salario >= 2 *desv  , ]$Salario
## [1] 11934688000 11563829000  1640178900  1119811900

Substituindo os outliers pela mediana:

#atualizamos todos para mediana
median(dados$Salario)
## [1] 8703250
dados[dados$Salario >= 2 *desv  , ]$Salario = median(dados$Salario)

Agora fazendo o gráfico:

ggplot(dados, aes(x=1, y=Salario))+ geom_boxplot()

Agora sim a base já está limpa e pronta para ser usada em algoritmos de Machine Learning.

Era isso.

Keep calm and analysing data!