Tópico 3 - Preparação da Base de Dados

Leitura

Nesta etapa precisamos ler o arquivo de dados. Em geral ele está salvo como o .csv, mas pode ser outros formatos também.

Antes de rodar o código abaixo, use a função setwd() para definir o diretório que se encontra o arquivo como o diretório corrente.

library(tidyverse)
dir()
##  [1] "base_teste.csv"            "base_teste.rds"           
##  [3] "base_treino.csv"           "base_treino.rds"          
##  [5] "base_treino_final.csv"     "base_treino_final.rds"    
##  [7] "insurance.csv"             "rsconnect"                
##  [9] "topico3-GET00217-script.R" "topico3-GET00217.html"    
## [11] "topico3-GET00217.Rmd"
base = read_csv("insurance.csv")
base
## # A tibble: 1,338 × 9
##      age sex      bmi children smoker region    charges married country
##    <dbl> <chr>  <dbl>    <dbl> <chr>  <chr>       <dbl> <chr>   <chr>  
##  1    19 female  27.9        0 yes    southwest  16885. yes     usa    
##  2    18 male    33.8        1 no     southeast   1726. <NA>    usa    
##  3    28 male    33          3 no     southeast   4449. yes     usa    
##  4    33 male    22.7        0 no     northwest  21984. no      usa    
##  5    32 male    28.9        0 no     northwest   3867. yes     usa    
##  6    31 female  25.7        0 no     southeast   3757. yes     usa    
##  7    46 female  33.4        1 no     southeast   8241. yes     usa    
##  8    37 female  27.7        3 no     northwest   7282. yes     usa    
##  9    37 male    29.8        2 no     northeast   6406. yes     usa    
## 10    60 female  25.8        0 no     northwest  28923. <NA>    usa    
## # ℹ 1,328 more rows
glimpse(base)
## Rows: 1,338
## Columns: 9
## $ age      <dbl> 19, 18, 28, 33, 32, 31, 46, 37, 37, 60, 25, 62, 23, 56, 27, 1…
## $ sex      <chr> "female", "male", "male", "male", "male", "female", "female",…
## $ bmi      <dbl> 27.900, 33.770, 33.000, 22.705, 28.880, 25.740, 33.440, 27.74…
## $ children <dbl> 0, 1, 3, 0, 0, 0, 1, 3, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0…
## $ smoker   <chr> "yes", "no", "no", "no", "no", "no", "no", "no", "no", "no", …
## $ region   <chr> "southwest", "southeast", "southeast", "northwest", "northwes…
## $ charges  <dbl> 16884.924, 1725.552, 4449.462, 21984.471, 3866.855, 3756.622,…
## $ married  <chr> "yes", NA, "yes", "no", "yes", "yes", "yes", "yes", "yes", NA…
## $ country  <chr> "usa", "usa", "usa", "usa", "usa", "usa", "usa", "usa", "usa"…
dim(base)
## [1] 1338    9

A base carregada tem 9 colunas e 1338 linhas. Cada linha (unidade amostral) representa um cliente e cada coluna (variável) uma característica desse cliente.

É preciso verificar quais as variáveis qualitativas e transformá-las para o tipo factor.

base$sex = as.factor(base$sex)
base$smoker = as.factor(base$smoker)
base$region = as.factor(base$region)
base$married = as.factor(base$married)
base$country = as.factor(base$country)
glimpse(base)
## Rows: 1,338
## Columns: 9
## $ age      <dbl> 19, 18, 28, 33, 32, 31, 46, 37, 37, 60, 25, 62, 23, 56, 27, 1…
## $ sex      <fct> female, male, male, male, male, female, female, female, male,…
## $ bmi      <dbl> 27.900, 33.770, 33.000, 22.705, 28.880, 25.740, 33.440, 27.74…
## $ children <dbl> 0, 1, 3, 0, 0, 0, 1, 3, 2, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0…
## $ smoker   <fct> yes, no, no, no, no, no, no, no, no, no, no, yes, no, no, yes…
## $ region   <fct> southwest, southeast, southeast, northwest, northwest, southe…
## $ charges  <dbl> 16884.924, 1725.552, 4449.462, 21984.471, 3866.855, 3756.622,…
## $ married  <fct> yes, NA, yes, no, yes, yes, yes, yes, yes, NA, NA, yes, NA, N…
## $ country  <fct> usa, usa, usa, usa, usa, usa, usa, usa, usa, usa, usa, usa, u…

Partição dos dados em treino e teste

Agora que a base já foi lida corretamente e as variáveis estão no seu formato ideal, vamos dividir a base em treino e teste. Todas as análises e ajustes dos modelos serão feitos na base de treino. A base de teste será usada somente mais pra frente, quando quisermos analisar o desempenho dos modelos com dados fora da amostra, isto é, dados que não foram usados no ajuste do modelo.

library(caret)
set.seed(123456789)

O pacote usado para isso é o caret. Para garantir a replicação do código é importante fixar a semente, já que o processo de partição da base envolve sorteios aleatórios.

N = nrow(base) #numero de linhas da base
indices_treino = createDataPartition(1:N,p=0.75)[[1]]
base_treino = base |> slice(indices_treino)
base_teste  = base |> slice(-indices_treino)

O hábito de salvar os objetos criados no R a cada etapa permite refazer algumas etapas sem precisar repetir as etapas iniciais, e, consequentemente, evita o retrabalho.

saveRDS(base_treino,file="base_treino.rds")
write_csv2(base_treino,file="base_treino.csv")

saveRDS(base_teste,file="base_teste.rds")
write_csv2(base_teste,file="base_teste.csv")

A partir de agora vamos usar somente a base de treino.

Análise de dados faltantes

Antes de fazer qualquer análise estatistica em uma base de dados é preciso organizar, limpar e conhecer essa base. Por exemplo, precisamos verificar se a base possui ou não dados faltantes.

summary(base_treino)
##       age            sex           bmi           children     smoker   
##  Min.   :18.00   female:494   Min.   :15.96   Min.   :0.000   no :802  
##  1st Qu.:27.00   male  :512   1st Qu.:26.22   1st Qu.:0.000   yes:204  
##  Median :40.00                Median :30.25   Median :1.000            
##  Mean   :39.39                Mean   :30.63   Mean   :1.087            
##  3rd Qu.:51.00                3rd Qu.:34.80   3rd Qu.:2.000            
##  Max.   :64.00                Max.   :53.13   Max.   :5.000            
##  NA's   :1                    NA's   :1                                
##        region       charges      married    country    
##  northeast:241   Min.   : 1122   no  :301   usa :1005  
##  northwest:241   1st Qu.: 4763   yes :307   NA's:   1  
##  southeast:280   Median : 9333   NA's:398              
##  southwest:244   Mean   :13183                         
##                  3rd Qu.:16584                         
##                  Max.   :63770                         
## 

O simples comando summary já nos mostra a existência ou não de dados faltantes. Mas a biblioteca naniar é mais adequada para entneder melhor onde estão esses dados e tomar a melhor decisão em relação à eles.

library(naniar)
base_treino |> gg_miss_var()

base_treino  |> miss_var_summary()
## # A tibble: 9 × 3
##   variable n_miss pct_miss
##   <chr>     <int>    <dbl>
## 1 married     398  39.6   
## 2 age           1   0.0994
## 3 bmi           1   0.0994
## 4 country       1   0.0994
## 5 sex           0   0     
## 6 children      0   0     
## 7 smoker        0   0     
## 8 region        0   0     
## 9 charges       0   0

Percebemos de cara que a variável married contém muitos dados faltantes. Quase 40% das entradas dessa variável são faltantes. Isso é um percentual muito grande que justifica a retirada da variável.

base_treino = base_treino |> select(-married)
base_treino |> gg_miss_var()

base_treino  |> miss_var_summary()
## # A tibble: 8 × 3
##   variable n_miss pct_miss
##   <chr>     <int>    <dbl>
## 1 age           1   0.0994
## 2 bmi           1   0.0994
## 3 country       1   0.0994
## 4 sex           0   0     
## 5 children      0   0     
## 6 smoker        0   0     
## 7 region        0   0     
## 8 charges       0   0

Ainda temos dados faltantes, mas muito poucos. Um em cada uma das seguintes variáveis: age, bmi e country. Podemos excluir as linhas que contém os dados faltantes ou substituir eles pela média, caso a variável seja quantitativa, ou a moda, caso a veriável seja qualitativa, da variável. Aqui optei por retirar as linhas com NA restantes.

indice_na = is.na(base_treino$bmi)
base_treino = base_treino |> filter(!indice_na)
indice_na = is.na(base_treino$age)
base_treino = base_treino |> filter(!indice_na)
indice_na = is.na(base_treino$country)
base_treino = base_treino |> filter(!indice_na)

Os gráficos agora nos mostram que não restou nenhuma NA na base.

base_treino |> gg_miss_var()

base_treino  |> miss_var_summary()
## # A tibble: 8 × 3
##   variable n_miss pct_miss
##   <chr>     <int>    <dbl>
## 1 age           0        0
## 2 sex           0        0
## 3 bmi           0        0
## 4 children      0        0
## 5 smoker        0        0
## 6 region        0        0
## 7 charges       0        0
## 8 country       0        0

Análise de correlação

As variáveis estão na base para caracterizar e diferenciar os indivíduos (unidade amostrais). Se duas variáveis apresentam uma correlação muito grande, significa que as duas caracterizam os indivíduos de forma semelhate. De acordo com o princípio da parcimônia, “Se existe mais de uma explicação para uma dada observação, devemos adotar aquela mais simples”, devemos ficar com uma das duas variáveis.

cor(base_treino |> select(age, bmi, children, charges))
##                 age        bmi   children    charges
## age      1.00000000 0.11507091 0.02382068 0.30716851
## bmi      0.11507091 1.00000000 0.01807609 0.17551576
## children 0.02382068 0.01807609 1.00000000 0.03028734
## charges  0.30716851 0.17551576 0.03028734 1.00000000

Identificação de variáveis com variabilidade (quase) zero

Outro caso que vamos analisar aqui é se existe uma variável com variablidade nula ou quase nula. Se a variável for a mesma, ou praticamente a mesma, entre todos os indivíduos, ela não agrega informação e também deve ser retirada

diag(cov(base_treino |> select(age, bmi, children, charges)))
##          age          bmi     children      charges 
## 1.960621e+02 3.790117e+01 1.448206e+00 1.414954e+08
summary(base_treino |> select(sex,smoker,region,country))
##      sex      smoker          region    country   
##  female:491   no :800   northeast:241   usa:1003  
##  male  :512   yes:203   northwest:239             
##                         southeast:280             
##                         southwest:243

Veja que a variável marriege assume o mesmo valor para todas as unidades amostrais. Dessa forma, tal variável deve ser retirada da base.

base_treino = base_treino |> select(-country)

Análise descritiva

Uma breve análise descritiva já nos diz muito sobre os dados e é muito importante de ser realizada antes de qualquer modelagem estatística.

hist(base_treino$age)

hist(base_treino$bmi)

hist(base_treino$children)

hist(base_treino$charges)

barplot(table(base_treino$sex))

barplot(table(base_treino$smoker))

barplot(table(base_treino$region))

Padronização das variaveis quantitativas

Para os métodos de redes neurais é sempre adequado padronizar as variáveies. Essa padronização pode ser feita de formas diferentes, como por exemplo,

\[ X_i = \dfrac{X_i - \bar{X}}{\sqrt{S^2}} \]

Que transforma a de forma que ela tenha média 0 e variância 1.

Outra possibilidade, muito usada também, é a transformação

\[X_i = \dfrac{X_i - \min(X)}{\max(X) - \min(X)}\]

que faz com que a variável transformada apresente valores somente entre 0 e 1. Esta opção é comum de ser usada para as variáveis de enteresse, que serão a saída da rede neural. Isso porque, em geral, a imagem da função de ativação de uma rede neural é um número limitado em um intervalo da reta. Nesse caso garantimos que a saída (transformada) será um número em \([0,1]\).

A transformação realizada aqui será a segunda apresentada. Mas poderia

base_treino_padronizada = base_treino |> mutate(
  age = (age - min(age))/(max(age)-min(age)), 
  bmi =  (bmi - min(bmi))/(max(bmi)-min(bmi)),
  children =  (children - min(children))/(max(children)-min(children)),
  charges =  (charges - min(charges))/(max(charges)-min(charges))
)

Tratamento nas variáveis categóricas

Em geral, as funções que rodam os métodos de redes neurais não aceita as variáveis qualitativas passadas como factor. É preciso criar variáveis indicadoras que representem de forma numérica estas variáveis qualitativas. Para isso será usada a função model.matrix

base_treino_final = as.matrix(model.matrix(~.,data = base_treino_padronizada)[,-1])
dim(base_treino_final)
## [1] 1003    9
colnames(base_treino_final)
## [1] "age"             "sexmale"         "bmi"             "children"       
## [5] "smokeryes"       "regionnorthwest" "regionsoutheast" "regionsouthwest"
## [9] "charges"

Salvar a base de treino final

COmo já comentado, é idnicado salvar os objetos criados a cada nova etapa do script.

saveRDS(base_treino_final,file="base_treino_final.rds")
write_csv(as.data.frame(base_treino_final),file="base_treino_final.csv")