Loại bỏ Outliers theo Cook’s Distance hoặc nằm ngoài 2 lần độ lệch chuẩn so với mean của phần dư

For Pleisure

Nguyễn Chí Dũng

Giới thiệu

Các phần tử bất thường (Outliers) có thể là nguyên nhân gây ra sự vi phạm các giả thiết của mô hình hồi quy tuyến tính và làm méo mó kết quả dự báo của mô hình.

Có hai tiêu chuẩn được sử dụng để đánh giá Outliers. Tiêu chuẩn thứ nhất (như phần mềm SPSS sử dụng) là bất cứ quan sát nào nằm ngoài 2 lần độ lệnh chuẩn so với mean của phần dư sẽ là các quan sát bất thường.

Tiêu chuẩn thứ hai là căn cứ vào khoản cách Cook - Cook’s Distance.

Kết quả chỉ ra rằng:

  1. Nếu loại Outliers theo tiêu chuẩn thứ nhất thì mức độ giải thích của mô hình tăng gần 5 lần.

  2. Nếu loại Outliers theo tiêu chuẩn thứ hai thì mức độ giải thích của mô hình tăng xấp xỉ 7 lần.

Số liệu sử dụng ở đây là bộ dữ liệu ceosal1 được sử dụng ở chương 2 cuốn Introductory Econometrics: A Modern Approach của Jeffrey Wooldridge.

# Load dữ liệu: 
rm(list = ls())
require(readstata13)
ceosal1 <- read.dta13("http://fmwww.bc.edu/ec-p/data/wooldridge/ceosal1.dta")

# Xem qua: 
library(tidyverse)
ceosal1 %>% head()
##   salary pcsalary   sales  roe pcroe ros indus finance consprod utility
## 1   1095       20 27595.0 14.1 106.4 191     1       0        0       0
## 2   1001       32  9958.0 10.9 -30.6  13     1       0        0       0
## 3   1122        9  6125.9 23.5 -16.3  14     1       0        0       0
## 4    578       -9 16246.0  5.9 -25.7 -21     1       0        0       0
## 5   1368        7 21783.2 13.8  -3.0  56     1       0        0       0
## 6   1145        5  6021.4 20.0   1.0  55     1       0        0       0
##    lsalary    lsales
## 1 6.998509 10.225389
## 2 6.908755  9.206132
## 3 7.022868  8.720281
## 4 6.359574  9.695602
## 5 7.221105  9.988894
## 6 7.043160  8.703075
#----------------------------------------------
#  Version 1: OLS Model với đầy đủ quán sát
#----------------------------------------------

ols1 <- ceosal1 %>% lm(salary ~ roe, data = .)
ols1 %>% summary()
## 
## Call:
## lm(formula = salary ~ roe, data = .)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1160.2  -526.0  -254.0   138.8 13499.9 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   963.19     213.24   4.517 1.05e-05 ***
## roe            18.50      11.12   1.663   0.0978 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1367 on 207 degrees of freedom
## Multiple R-squared:  0.01319,    Adjusted R-squared:  0.008421 
## F-statistic: 2.767 on 1 and 207 DF,  p-value: 0.09777
theme_set(theme_minimal())
ceosal1 %>% 
  ggplot(aes(roe, salary)) + 
  geom_point(alpha = 0.3) + 
  geom_smooth(method = "lm")

# Bằng mắt thường chúng ta có thể thấy có một số quan sát
# có mức lương rất cao. Chúng là những outliers. Nhưng ta
# đã biết outliers có ảnh hưởng rất mạnh đến kết quả  của
# OLS. Dưới đây chúng ta sẽ tìm cách loại bỏ các outliers 
# theo một số tiêu chuẩn  thống kê và thực hiện lại OLS. 
# Trước hết chúng ta chẩn đoán các lỗi có thể có của mô 
# hình cũng như một số quan sát bất thường: 

library("ggfortify")
autoplot(ols1, which = 1:6, ncol = 3, label.size = 1)

#------------------------------------------------------
#  Version 2: Loại bỏ outliers theo tiêu chuẩn
#  nằm ngoài 2 lần độ lệch chuẩn so với mean phần dư
#-------------------------------------------------------

ct <- mean(ols1$residuals) + 2*sd(ols1$residuals)
cd <- mean(ols1$residuals) - 2*sd(ols1$residuals)

library(magrittr)
ceosal1 %<>% 
  mutate(phan_du = ols1$residuals, 
         out = case_when(phan_du > ct ~ "Outlier", 
                         phan_du < cd ~ "Outlier", 
                         phan_du <= ct & phan_du >= cd ~ "Normal"))

# Các quan sát là outliers theo tiêu chuẩn này: 
ceosal1 %>% filter(out == "Outlier")
##   salary pcsalary  sales  roe pcroe ros indus finance consprod utility
## 1   4143      -10 2678.4 14.4 -12.8  42     0       1        0       0
## 2   6640        4 8946.0 10.2  87.6  34     0       1        0       0
## 3  11233       17 6047.9 22.9   9.9  74     0       0        1       0
## 4  14822        1 2159.2 19.4 -37.3  17     0       0        1       0
##    lsalary   lsales   phan_du     out
## 1 8.329175 7.892975  2913.392 Outlier
## 2 8.800867 9.098962  5488.097 Outlier
## 3 9.326612 8.707466  9846.132 Outlier
## 4 9.603868 7.677493 13499.886 Outlier
# Nếu chúng ta loại bỏ chúng ra và thực  hiện lại OLS thì: 
ols2 <- ceosal1 %>% 
  filter(out != "Outlier") %>% 
  lm(salary ~ roe, data = .)

ols2 %>% summary()
## 
## Call:
## lm(formula = salary ~ roe, data = .)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -990.8 -372.2 -104.5  266.9 2798.4 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  853.563     87.866   9.714  < 2e-16 ***
## roe           15.870      4.576   3.468 0.000639 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 560.4 on 203 degrees of freedom
## Multiple R-squared:  0.05595,    Adjusted R-squared:  0.0513 
## F-statistic: 12.03 on 1 and 203 DF,  p-value: 0.000639
# Đường hồi quy của mô hình này cũng như minh họa đủ mọi thứ trong 
# đó biểu diễn các outliers, đường màu blue là đường hồi quy đủ, 
# đường green là đường hồi quy đã bỏ đi 4 (hay 1.9% quan sát) là outliers: 
library(ggrepel)
ceosal1 %>% 
  ggplot(aes(roe, salary)) + 
  geom_point(data = ceosal1 %>% filter(out == "Outlier"), 
             color = "red", size = 2) + 
  geom_smooth(method = "lm", se = FALSE) + 
  geom_point(data = ceosal1 %>% filter(out != "Outlier"), 
             color = "purple", alpha = 0.3) + 
  geom_smooth(data = ceosal1 %>% filter(out != "Outlier"), 
              method = "lm", se = FALSE, color = "green") + 
  geom_text_repel(aes(label = out), 
            data = ceosal1 %>% filter(out == "Outlier"), force = 19)

# Có thể thấy là sau khi loại bỏ 4 quan sát này thì kết quả OLS
# thay đổi đáng kể. Thứ nhất là hệ số góc: 

ols1$coefficients # Mô hình ban đầu. 
## (Intercept)         roe 
##   963.19134    18.50119
ols2$coefficients # Mô hình loại bỏ 4 quan sát bất thường. 
## (Intercept)         roe 
##   853.56290    15.87006
# Đặc biệt là mức độ giải thích của mô hình thay đổi rất lớn. 
# Cụ thể mô hình sau khi bỏ outliers có khả năng giải thích tăng 5 lần: 

ols1 %>% summary() -> u
u$r.squared # Mô hình ban đầu. 
## [1] 0.01318862
ols2 %>% summary() -> v
v$r.squared # Mô hình bỏ bốn outliers. 
## [1] 0.05594679
# Nghĩa là mô hình  2 nếu được sử dụng để dự báo mức lương 
# phụ thuộc ra sao vào roe - hiệu quả điều hành công ti của 
# các CEO thì  sẽ dự báo chính  xác hơn so với  mô hình 1. 


#---------------------------------------------------------------
#   Version 3: Loại bỏ outlier theo
#   khoảng cách Cook (Cook’s Distance)
#   Đọc thêm: https://en.wikipedia.org/wiki/Cook%27s_distance
#---------------------------------------------------------------

# Tính  khoản cách Cook: 
cut_off <- 1 / nrow(ceosal1)

# Xếp loại quan sát theo tiêu chuẩn khoảng cách Cook: 
ceosal1 %<>% mutate(d = cooks.distance(ols1),  
                    out_d = case_when(d > cut_off ~ "Outlier", 
                                      d <= cut_off ~ "Normal"))

# Các quan sát là bất thường theo tiêu chuẩn Cook: 
ceosal1 %>% 
  filter(out_d == "Outlier") %>% 
  select(-indus, -finance, -phan_du, -d)
##   salary pcsalary   sales  roe pcroe ros consprod utility  lsalary
## 1   3844       61 20604.0 12.1  -5.9  18        0       0 8.254269
## 2   4143      -10  2678.4 14.4 -12.8  42        0       0 8.329175
## 3   6640        4  8946.0 10.2  87.6  34        0       0 8.800867
## 4    612       39  3618.9 33.3 -10.5  68        0       0 6.416732
## 5   3142       15 10236.3 35.7  27.2 155        1       0 8.052615
## 6   1130        1  1686.3 44.5   3.4  99        1       0 7.029973
## 7  11233       17  6047.9 22.9   9.9  74        1       0 9.326612
## 8   3646      -11  3921.5  7.8 -64.2  29        1       0 8.201385
## 9  14822        1  2159.2 19.4 -37.3  17        1       0 9.603868
##     lsales     out   out_d
## 1 9.933241  Normal Outlier
## 2 7.892975 Outlier Outlier
## 3 9.098962 Outlier Outlier
## 4 8.193925  Normal Outlier
## 5 9.233695  Normal Outlier
## 6 7.430292  Normal Outlier
## 7 8.707466 Outlier Outlier
## 8 8.274229  Normal Outlier
## 9 7.677493 Outlier Outlier
# Theo tiêu chuẩn này thì số các quán sát bất thường là chặt hơn: có 
# 9 quan sát tức tương đương 4.3 % kích thước mẫu ban đầu. Thực  hiện
# hồi quy sau khi loại outlier theo tiêu chuẩn Cook: 

ols3 <- ceosal1 %>% 
  filter(out_d != "Outlier") %>%
  lm(salary ~ roe, data = .)

# Kết quả của  mô hình OLS này cho thấy mức độ giải thích của  mô 
# hình  (nếu  căn cứ vào R2) cao gấp gần 7 lần (0.09001 so với 0.01318862): 
ols3 %>% summary()
## 
## Call:
## lm(formula = salary ~ roe, data = .)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -973.68 -340.29  -71.92  289.09 1893.04 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   785.79      77.08  10.195  < 2e-16 ***
## roe            18.10       4.09   4.425 1.59e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 475.7 on 198 degrees of freedom
## Multiple R-squared:  0.09001,    Adjusted R-squared:  0.08541 
## F-statistic: 19.58 on 1 and 198 DF,  p-value: 1.588e-05
# Có thể hình ảnh hóa các điểm với khoảng cách Cook tương ứng:  

autoplot(ols1, which = 4, ncol = 3, label.size = 1) # Cách 1. 

# Tại cột biến N chỉ thị số thứ tự của  quan sát: 
u <- ceosal1  %>%  
  mutate(N = 1:nrow(.))

u %>% 
  ggplot(aes(N, d)) +
  geom_point(alpha = 0.2) + 
  geom_point(data = u %>% filter(d > cut_off), color = "red") + 
  geom_text_repel(data = u %>% filter(d > cut_off),  
                  aes(label = N), force = 19) + 
  labs(x = NULL, y = "Cook's  Distance", 
       title = "Outliers based on Cook's Distance") #  Cách 2. 

#-------------------------------------------
#               Mở rộng
#-------------------------------------------

# Tất cả các bước dài dòng trên có thể được làm ngắn gọn như sau 
# để so sánh các mô hình. Trước hết tạo ra ba bộ số liệu như sau: 

df1 <- ceosal1 %>% 
  select(salary, roe) %>% 
  mutate(Model = "All") #  Bộ số liệu nguyên bản. 

df2 <- ceosal1 %>% 
  filter(out != "Outlier") %>% 
  select(salary, roe) %>% 
  mutate(Model = "Remove4") # Bộ bỏ 4. 

df3 <- ceosal1 %>% 
  filter(out_d != "Outlier") %>% 
  select(salary, roe) %>% 
  mutate(Model = "Remove9") # Bộ bỏ 9. 


# Hợp nhất cả ba bộ dữ liệu: 
all_df <- bind_rows(df1, df2, df3)

library(purrr)

# So sánh kết quả hồi quy của ba phiên bản: 
all_df %>% 
  split(.$Model) %>% 
  map(function(df) lm(salary ~ roe, data = df)) %>% 
  map(summary)
## $All
## 
## Call:
## lm(formula = salary ~ roe, data = df)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -1160.2  -526.0  -254.0   138.8 13499.9 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   963.19     213.24   4.517 1.05e-05 ***
## roe            18.50      11.12   1.663   0.0978 .  
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1367 on 207 degrees of freedom
## Multiple R-squared:  0.01319,    Adjusted R-squared:  0.008421 
## F-statistic: 2.767 on 1 and 207 DF,  p-value: 0.09777
## 
## 
## $Remove4
## 
## Call:
## lm(formula = salary ~ roe, data = df)
## 
## Residuals:
##    Min     1Q Median     3Q    Max 
## -990.8 -372.2 -104.5  266.9 2798.4 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  853.563     87.866   9.714  < 2e-16 ***
## roe           15.870      4.576   3.468 0.000639 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 560.4 on 203 degrees of freedom
## Multiple R-squared:  0.05595,    Adjusted R-squared:  0.0513 
## F-statistic: 12.03 on 1 and 203 DF,  p-value: 0.000639
## 
## 
## $Remove9
## 
## Call:
## lm(formula = salary ~ roe, data = df)
## 
## Residuals:
##     Min      1Q  Median      3Q     Max 
## -973.68 -340.29  -71.92  289.09 1893.04 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept)   785.79      77.08  10.195  < 2e-16 ***
## roe            18.10       4.09   4.425 1.59e-05 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 475.7 on 198 degrees of freedom
## Multiple R-squared:  0.09001,    Adjusted R-squared:  0.08541 
## F-statistic: 19.58 on 1 and 198 DF,  p-value: 1.588e-05
# So sánh hệ số R2: 
all_df %>% 
  split(.$Model) %>% 
  map(function(df) lm(salary ~ roe, data = df)) %>% 
  map(summary) %>% 
  map_dbl("r.squared")
##        All    Remove4    Remove9 
## 0.01318862 0.05594679 0.09000992
# Hay so sánh các hệ số hồi quy: 
all_df %>% 
  split(.$Model) %>% 
  map(function(df) lm(salary ~ roe, data = df)) %>% 
  map(summary) %>% 
  map("coefficients")
## $All
##              Estimate Std. Error  t value     Pr(>|t|)
## (Intercept) 963.19134  213.24026 4.516930 1.053352e-05
## roe          18.50119   11.12325 1.663289 9.776775e-02
## 
## $Remove4
##              Estimate Std. Error  t value     Pr(>|t|)
## (Intercept) 853.56290  87.865950 9.714376 1.458024e-18
## roe          15.87006   4.575527 3.468466 6.390450e-04
## 
## $Remove9
##              Estimate Std. Error  t value     Pr(>|t|)
## (Intercept) 785.79549  77.078456 10.19475 6.907394e-20
## roe          18.10064   4.090106  4.42547 1.587915e-05