Capítulo 3 Regressão

library(magrittr)
library(tidyverse)
library(GGally)
library(forecast)
library(broom)
library(ggalt)
library(ggExtra)

A técnica chamada de regressão é usada para predizer o valor de uma variável Y (chamada de variável resposta ou dependente) baseado em uma ou mais variáveis X (variável explanatória ou independente). Se a regressão utiliza apenas uma variável explanatória, é chamada de regressão simples. O objetivo da regressão é representar a relação entre as variáveis resposta e explanatória por meio de uma equação matemática linear do tipo:

$Y = _1 + _2X + $

onde \(\beta_1\) é a interceptação da reta com o eixo vertical e \(\beta_2\) o coeficiente de inclinação associado à variável explanatória. Tais elementos são chamados coeficientes da regressão. O termo \(\epsilon\) representa o termo do erro, que é a parte de Y que a regressão é incapaz de explicar (por existir outras variáveis que explicariam Y mas que não foram incorporadas ao modelo).

Neste capítulo, usaremos como exemplo o dataset x15 extraído deste site, que traz dados que tentam explicar o consumo de petróleo baseado em outros inputs. Os dados não vêm já formatados, o que vai acabar servindo para mostrar um exemplo de limpeza de dados em R.

url <- "http://people.sc.fsu.edu/~jburkardt/datasets/regression/x15.txt"
readLines(url)
##  [1] "#  x15.txt"                                                             
##  [2] "#"                                                                      
##  [3] "#  Reference:"                                                          
##  [4] "#"                                                                      
##  [5] "#    Helmut Spaeth,"                                                    
##  [6] "#    Mathematical Algorithms for Linear Regression,"                    
##  [7] "#    Academic Press, 1991,"                                             
##  [8] "#    ISBN 0-12-656460-4."                                               
##  [9] "#"                                                                      
## [10] "#    S Weisberg,"                                                       
## [11] "#    Applied Linear Regression,"                                        
## [12] "#    New York, 1980, pages 32-33."                                      
## [13] "#"                                                                      
## [14] "#  Discussion:"                                                         
## [15] "#"                                                                      
## [16] "#    For one year, the consumption of petrol was measured in 48 states."
## [17] "#    The relevant variables are the petrol tax, the average"            
## [18] "#    income per capita, the number of miles of paved highway, and"      
## [19] "#    the proportion of the population with driver's licenses."          
## [20] "#"                                                                      
## [21] "#    There are 48 rows of data.  The data include:"                     
## [22] "#"                                                                      
## [23] "#      I,  the index;"                                                  
## [24] "#      A1, the petrol tax;"                                             
## [25] "#      A2, the per capita income;"                                      
## [26] "#      A3, the miles of paved highway;"                                 
## [27] "#      A4, the proportion of drivers;"                                  
## [28] "#      B,  the consumption of petrol."                                  
## [29] "#"                                                                      
## [30] "#    We seek a model of the form:"                                      
## [31] "#"                                                                      
## [32] "#      B = A1 * X1 + A2 * X2 + A3 * X3 + A4 * X4."                      
## [33] "#"                                                                      
## [34] "6 columns"                                                              
## [35] "48 rows"                                                                
## [36] "Index"                                                                  
## [37] "Petrol tax (cents per gallon)"                                          
## [38] "Average income (dollars)"                                               
## [39] "Paved Highways (miles)"                                                 
## [40] "Proportion of population with driver's licenses"                        
## [41] "Consumption of petrol (millions of gallons)"                            
## [42] " 1   9.00  3571   1976  0.5250  541"                                    
## [43] " 2   9.00  4092   1250  0.5720  524"                                    
## [44] " 3   9.00  3865   1586  0.5800  561"                                    
## [45] " 4   7.50  4870   2351  0.5290  414"                                    
## [46] " 5   8.00  4399    431  0.5440  410"                                    
## [47] " 6  10.00  5342   1333  0.5710  457"                                    
## [48] " 7   8.00  5319  11868  0.4510  344"                                    
## [49] " 8   8.00  5126   2138  0.5530  467"                                    
## [50] " 9   8.00  4447   8577  0.5290  464"                                    
## [51] "10   7.00  4512   8507  0.5520  498"                                    
## [52] "11   8.00  4391   5939  0.5300  580"                                    
## [53] "12   7.50  5126  14186  0.5250  471"                                    
## [54] "13   7.00  4817   6930  0.5740  525"                                    
## [55] "14   7.00  4207   6580  0.5450  508"                                    
## [56] "15   7.00  4332   8159  0.6080  566"                                    
## [57] "16   7.00  4318  10340  0.5860  635"                                    
## [58] "17   7.00  4206   8508  0.5720  603"                                    
## [59] "18   7.00  3718   4725  0.5400  714"                                    
## [60] "19   7.00  4716   5915  0.7240  865"                                    
## [61] "20   8.50  4341   6010  0.6770  640"                                    
## [62] "21   7.00  4593   7834  0.6630  649"                                    
## [63] "22   8.00  4983    602  0.6020  540"                                    
## [64] "23   9.00  4897   2449  0.5110  464"                                    
## [65] "24   9.00  4258   4686  0.5170  547"                                    
## [66] "25   8.50  4574   2619  0.5510  460"                                    
## [67] "26   9.00  3721   4746  0.5440  566"                                    
## [68] "27   8.00  3448   5399  0.5480  577"                                    
## [69] "28   7.50  3846   9061  0.5790  631"                                    
## [70] "29   8.00  4188   5975  0.5630  574"                                    
## [71] "30   9.00  3601   4650  0.4930  534"                                    
## [72] "31   7.00  3640   6905  0.5180  571"                                    
## [73] "32   7.00  3333   6594  0.5130  554"                                    
## [74] "33   8.00  3063   6524  0.5780  577"                                    
## [75] "34   7.50  3357   4121  0.5470  628"                                    
## [76] "35   8.00  3528   3495  0.4870  487"                                    
## [77] "36   6.58  3802   7834  0.6290  644"                                    
## [78] "37   5.00  4045  17782  0.5660  640"                                    
## [79] "38   7.00  3897   6385  0.5860  704"                                    
## [80] "39   8.50  3635   3274  0.6630  648"                                    
## [81] "40   7.00  4345   3905  0.6720  968"                                    
## [82] "41   7.00  4449   4639  0.6260  587"                                    
## [83] "42   7.00  3656   3985  0.5630  699"                                    
## [84] "43   7.00  4300   3635  0.6030  632"                                    
## [85] "44   7.00  3745   2611  0.5080  591"                                    
## [86] "45   6.00  5215   2302  0.6720  782"                                    
## [87] "46   9.00  4476   3942  0.5710  510"                                    
## [88] "47   7.00  4296   4083  0.6230  610"                                    
## [89] "48   7.00  5002   9794  0.5930  524"                                    
## [90] ""
# Os dados de verdade estão presentes entre as linhas 42 a 89. 
# Os nomes das colunas estão presentes entre as linhas 36 a 41
dados <- readLines(url)[42:89]
# como salvamos o dataset como se fosse um string em uma variavel...
# (ao inves de importar de um arquivo de texto), precisamos indicar que a variavel...
# dados corresponde a um arquivo de texto que contem um dataset
con <- textConnection(dados)
df <- read.table(con, header = FALSE)
close(con)
# adicionando o nome das colunas
names(df) <- c("linha", "imposto", "renda_media", "km_asfaltado", "n_carteiras",
               "consumo")
# retirar a primeira coluna já que nao serve para nada
df <- df[,-1]

head(df)
##   imposto renda_media km_asfaltado n_carteiras consumo
## 1     9.0        3571         1976       0.525     541
## 2     9.0        4092         1250       0.572     524
## 3     9.0        3865         1586       0.580     561
## 4     7.5        4870         2351       0.529     414
## 5     8.0        4399          431       0.544     410
## 6    10.0        5342         1333       0.571     457

O objetivo aqui é descobrir qual das 4 variáveis explica a variável consumo.

3.1 Correlação

Correlação é um indicador estatístico que mede o nível de dependência linear entre duas variáveis. Está definida no intervalo [-1, +1]. Se a correlação é negativa, indica que as variáveis são inversamente proporcinais: quando uma aumenta, a outra diminui. Se é positiva, indica que as variáveis são diretamente proporcionais.

Medir a correlação no R é muito simples:

# Usando a função cor
cor(df$consumo, df$n_carteiras)
## [1] 0.6989654
cor(df$consumo, df$renda_media)
## [1] -0.2448621

Para analisar todas as correlações entre todas as variáveis de uma só vez, podemos tanto calcular uma matriz de correlação como plotar todas as correlações com o auxílio do pacote GGally:

# matriz de correlacao:
cor(df)
##                  imposto renda_media km_asfaltado n_carteiras     consumo
## imposto       1.00000000  0.01266516  -0.52213014  -0.2880372 -0.45128028
## renda_media   0.01266516  1.00000000   0.05016279   0.1570701 -0.24486207
## km_asfaltado -0.52213014  0.05016279   1.00000000  -0.0641295  0.01904194
## n_carteiras  -0.28803717  0.15707008  -0.06412950   1.0000000  0.69896542
## consumo      -0.45128028 -0.24486207   0.01904194   0.6989654  1.00000000
# usando o pacote GGally
ggpairs(df)

Percebe-se pela matriz de correlação (e principalmente pelo gráfico) que só valeria a pena usar como variáveis explanatórias do nosso objeto de estudo o número de carteiras de habilitação e talvez o imposto sobre o consumo de petróleo.

3.2 Análise explatória e gráfica

No gráfico acima, mais precisamente na parte referente ao gráfico de dispersão entre as variáveis consumo e número de carteiras, é notório que existem quatro pontos que se destacam dos demais: três para cima e um para baixo. Como eles estão distantes verticalmente do “bolo” de pontos, o que é um indício de que são outliers em consumo. Tratamento de outliers é um tópico fundamental na construção de modelos.

Primeiramente, vamos analisar a distribuição da variável de consumo por meio de um histograma:

# pelo ggplot2, o grafico fica com intervalos meio feios:
ggplot(df, aes(x = consumo)) +
  geom_histogram() +
  labs(x = "Consumo", y = "Quantidade")
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

# o pacote forecast traz uma versao melhorada do histograma do ggplot2:
gghistogram(df$consumo) +
  labs(x = "Consumo", y = "Quantidade")

O histograma confirma que os valores acima de 800 peças e abaixo de 400 são anomalias dada a distribuição normal que a variável consumo aparenta ter.

Visto que é possível descrever consumo como uma distribuição normal, pode-se assumir que a probabilidade de que um valor esteja fora do intervalo \(\mu(consumo) \pm 3 \times \sigma(consumo)\) é de apenas 0,27%. Vamos destacar esses outliers com o auxílio dos pacotes ggalt e ggExtra, que são extensões do ggplot2.

# calcular limites superior e inferior
lim_sup <- mean(df$consumo) + 2 * sd(df$consumo)
lim_inf <- mean(df$consumo) - 2 * sd(df$consumo)

# por curiosidade, como ficaria com o pipe:
lim_sup <- df$consumo %>% {mean(.) + 3 * sd(.)}
lim_inf <- df$consumo %>% {mean(.) - 3 * sd(.)}
# criar dataframe sem outliers
df_sem_out <- df %>% filter(lim_inf < consumo & consumo < lim_sup)


p <- ggplot(df, aes(x = n_carteiras, y = consumo)) +
  geom_point() + 
  # adicionar curva da regressao
  geom_smooth(method = "lm") +
  # plotar circulo que deixe de fora os outliers
  geom_encircle(data = df_sem_out,
                aes(x = n_carteiras, y = consumo), color = 'red') + 
  labs(x = "Número de carteiras de motorista", y = "Consumo de petróleo")

# plotar histogramas nas margens do grafico
ggMarginal(p, type = "histogram")

Agora o outlier onde consumo > 800 e n_carteiras > 0,65 ficou mais visível. Portanto, faz sentido removê-lo da análise.

Como ficam as correlacões sem o outlier?

# comparar as matrizes de correlacao
cor(df)
##                  imposto renda_media km_asfaltado n_carteiras     consumo
## imposto       1.00000000  0.01266516  -0.52213014  -0.2880372 -0.45128028
## renda_media   0.01266516  1.00000000   0.05016279   0.1570701 -0.24486207
## km_asfaltado -0.52213014  0.05016279   1.00000000  -0.0641295  0.01904194
## n_carteiras  -0.28803717  0.15707008  -0.06412950   1.0000000  0.69896542
## consumo      -0.45128028 -0.24486207   0.01904194   0.6989654  1.00000000
cor(df_sem_out)
##                  imposto renda_media km_asfaltado n_carteiras     consumo
## imposto       1.00000000  0.01550113  -0.53357179 -0.27154780 -0.46681324
## renda_media   0.01550113  1.00000000   0.05216791  0.15575222 -0.30179781
## km_asfaltado -0.53357179  0.05216791   1.00000000 -0.04705121  0.06454609
## n_carteiras  -0.27154780  0.15575222  -0.04705121  1.00000000  0.67838583
## consumo      -0.46681324 -0.30179781   0.06454609  0.67838583  1.00000000

3.3 Modelagem por regressão simples

No R, é bem simples ajustar um modelo de regressão. Usando a variável n_carteiras como explanatória e consumo como resposta, um modelo é construído da seguinte maneira:

modelo.simples <- lm(consumo ~ n_carteiras, data = df_sem_out)
summary(modelo.simples)
## 
## Call:
## lm(formula = consumo ~ n_carteiras, data = df_sem_out)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -129.011  -51.414   -3.418   51.053  179.860 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   -123.5      112.2  -1.101    0.277    
## n_carteiras   1217.8      196.6   6.194 1.61e-07 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 71.99 on 45 degrees of freedom
## Multiple R-squared:  0.4602, Adjusted R-squared:  0.4482 
## F-statistic: 38.37 on 1 and 45 DF,  p-value: 1.608e-07

Com o modelo criado, é possível descrever a relação entre consumo e n_carteiras matematicamente por meio da seguinte equação:

\(consumo = -123.5 + 1217.8 \times n\_carteiras\)

Vamos deixar para analisar os diagnósticos da regressão no próximo item:

3.4 Regressão multivariada

Suponha também que você deseja incorporar a variável imposto ao modelo. Antes de fazer isso, vamos ver como as duas variáveis explanatórias se relacionam com a resposta em um gráfico tridimensional:

ggplot(df, aes(x = n_carteiras, y = consumo, color = imposto)) +
  geom_point() +
  scale_color_continuous(low = "green",  high = "red")

Valores altos de impostos aparentam estar associados com valores baixos de consumo.

Para adicionar uma nova variável ao modelo, fazemos:

modelo.mult <- lm(consumo ~ n_carteiras + imposto, data = df_sem_out)
summary(modelo.mult)
## 
## Call:
## lm(formula = consumo ~ n_carteiras + imposto, data = df_sem_out)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -122.786  -55.792    3.455   47.914  154.557 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   198.65     152.41   1.303  0.19920    
## n_carteiras  1069.12     189.39   5.645 1.12e-06 ***
## imposto       -30.93      10.70  -2.892  0.00593 ** 
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 66.74 on 44 degrees of freedom
## Multiple R-squared:  0.5464, Adjusted R-squared:  0.5258 
## F-statistic:  26.5 on 2 and 44 DF,  p-value: 2.794e-08
# Usando o pacote broom para formatar o output dos modelos de regressao
# concatenando os dois modelos em um dataframe so

# metricas dos regressores
modelo.simples %>% tidy
##          term  estimate std.error statistic      p.value
## 1 (Intercept) -123.4979  112.2050 -1.100646 2.769038e-01
## 2 n_carteiras 1217.8476  196.6181  6.193976 1.608027e-07
modelo.mult %>% tidy
##          term   estimate std.error statistic      p.value
## 1 (Intercept)  198.65147 152.40539  1.303441 1.992035e-01
## 2 n_carteiras 1069.11730 189.38537  5.645195 1.118216e-06
## 3     imposto  -30.93311  10.69589 -2.892056 5.928055e-03
# metricas do modelo
rbind(modelo.simples %>% glance,
     modelo.mult %>% glance)
##   r.squared adj.r.squared    sigma statistic      p.value df    logLik
## 1 0.4602073     0.4482119 71.99031  38.36534 1.608027e-07  2 -266.6652
## 2 0.5464273     0.5258103 66.73658  26.50380 2.794040e-08  3 -262.5755
##        AIC      BIC deviance df.residual
## 1 539.3304 544.8808 233217.2          45
## 2 533.1510 540.5516 195965.9          44

Agora vamos à análise dos indicadores da regressão:

3.4.1 Hipótese nula da regressão

A presença de um valor-p indica que existe uma hipótese nula sendo testada. Na regressão linear, a hipótese nula é a de que os coeficientes das variáveis explanatórias são iguais a zero. A hipótese alternativa é a de que os coeficientes não são iguais a zero, ou seja, existe uma relação matemático entre as variáveis do modelo.

3.4.2 valor-p

Nós podemos considerar um modelo linear estatisticamente significante apenas se os valores-p, tanto dos coeficientes como do modelo, são menores que um nível de significância pré-determinado, que idealmente é 0,05.

3.4.3 valor-t ou estatística

Um valor grande do valor-t indica que é pouco provável que os coeficientes não sejam iguais a zero puramente por coincidência. Assim, quanto maior o valor-t, melhor.

3.4.4 R-quadrado e R-quadrado ajustado

R-quadrado é a proporção da variação da variável resposta que é explicada pelo modelo. Quanto maior, melhor o modelo, supostamente.

Se continuarmos adicionando variáveis ao modelo de regressão, o R-quadrado apenas tende a crescer, intuitivamente. Isso acontecerá mesmo que a variável explanatória adicionada não seja significante. Para evitar esse problema que tornaria a comparação entre modelos praticamente inviável, o R-quadrado ajustado “penaliza” o valor do R-quadrado pelo número de variáveis adicionadas. Semelhantemente ao R-quadrado, quanto maior, melhor.

3.4.5 AIC e BIC

O AIC (Critério de Informação de Akaike) e o BIC (Critério de Informação Bayesiano) são métricas de qualidade de ajuste de um modelo estatístico que podem ser usados para a seleção de modelos. Quanto menor os valores, melhor o modelo.

3.4.6 Análise dos resíduos

Um indicador visual da qualidade de um modelo é a distribuição dos modelos: um bom modelo apresentará resídos que seguem uma distribuição normal com média 0.

Um modelo de regressão pressupõe que seus resíduos (subtração entre o valor real e o ajustado) seguem uma distribuição normal e não possuem nenhum tipo de relação matemática com os regressores do modelo (ou mesmo com variáveis independentes não usadas no modelo).

Para analisar o primeiro pressuposto do modelo múltiplo, fazemos um histograma dos resíduos:

modelo.mult$residuals %>% hist

Para analisar o segundo pressuposto, plotamos os resíduos contra todas as variáveis do dataset:

par(mfrow=c(2,2))
plot(df_sem_out$imposto, residuals(modelo.mult), xlab = "imposto")
plot(df_sem_out$renda_media, residuals(modelo.mult), xlab = "renda media")
plot(df_sem_out$km_asfaltado, residuals(modelo.mult), xlab = "km asfaltado")
plot(df_sem_out$n_carteiras, residuals(modelo.mult), xlab = "n_carteiras")

Os resíduos do modelo múltiplo aparentam ter alguma relação com a variável renda_media, o que indica que ela pode ser incorporada ao modelo.

3.5 Regressão como modelo preditivo

Um dos objetivos da regressão, além de descrever matematicamente a relação entre duas ou mais variáveis, é prever o valor da variável dependente baseado em novos valores da variável independente. Não é possível afirmar que um modelo apresentará um bom desempenho preditivo analisando apenas as métricas da regressão do tópico anterior. É necessário testar o modelo em dados que ele nunca viu.

A prática comum em Data Science é de separar o conjunto de dados que se tem em mãos em dois conjuntos: o de treino, que será usado para construir o modelo, e o de teste, que será usado como input do modelo para avaliar sua acurácia.

Após obter as previsões, deve-se usar uma ou mais métricas de erro (ver capítulo posterior) para avaliar a precisão do modelo.

set.seed(1993)  # escolhendo uma seed para tornar os resultados previsiveis
indice <- sample(1:nrow(df_sem_out), 0.8*nrow(df_sem_out))  # row indices for training data
treino <- df_sem_out[indice, ]  # model training data
teste  <- df_sem_out[-indice, ]   # test data

# construindo os dois modelos, mantendo o teste de fora
modelo.simples <- lm(consumo ~ n_carteiras, data = treino)
modelo.mult <- lm(consumo ~ n_carteiras + imposto, data = treino)

# calcular previsao baseado no dataframe de teste
prev.simples <- predict(modelo.simples, teste)
prev.mult <- predict(modelo.mult, teste)
# uma das metricas é correlação entre previsto e real:
real = teste$consumo

cor(prev.simples, real)
## [1] 0.8841406
cor(prev.mult, real)
## [1] 0.8611087
# outra metrica é o MAPE
mean(abs(prev.simples - real)/real)
## [1] 0.1101031
mean(abs(prev.mult - real)/real)
## [1] 0.1134987

Os dois modelos apresentam resultados semelhantes de erro. Portanto, pelo menos para este teste, não houve um aumento significativo de acurácia no modelo ao incorporar a variável imposto como explanatória.

3.6 Referências