Chuyển đến nội dung chính

Hướng dẫn Bảng chéo trong R

Trong hướng dẫn này, bạn sẽ học cách tạo bảng chéo và cách kiểm định, định lượng các mối quan hệ thể hiện trong đó.
· 10 phút đọc

Khám phá cùng AI

Cách tạo một bảng

Trước hết, hãy lấy một ít dữ liệu. Gói MASS chứa dữ liệu về 93 mẫu xe bán tại Mỹ vào năm 1993. Chúng được lưu trong đối tượng Cars93 và bao gồm 27 thuộc tính cho mỗi xe, một số là biến phân loại. Vậy hãy nạp gói MASS và xem loại phương tiện có trong cars93:

library(MASS)
Cars93$Type

##  [1] Small   Midsize Compact Midsize Midsize Midsize Large   Large  
##  [9] Midsize Large   Midsize Compact Compact Sporty  Midsize Van    
## [17] Van     Large   Sporty  Large   Compact Large   Small   Small  
## [25] Compact Van     Midsize Sporty  Small   Large   Small   Small  
## [33] Compact Sporty  Sporty  Van     Midsize Large   Small   Sporty
## [41] Sporty  Small   Compact Small   Small   Sporty  Midsize Midsize
## [49] Midsize Midsize Midsize Large   Small   Small   Compact Van    
## [57] Sporty  Compact Midsize Sporty  Midsize Small   Midsize Small  
## [65] Compact Van     Midsize Compact Midsize Van     Large   Sporty
## [73] Small   Compact Sporty  Midsize Large   Compact Small   Small  
## [81] Small   Compact Small   Small   Sporty  Midsize Van     Small  
## [89] Van     Compact Sporty  Compact Midsize
## Levels: Compact Large Midsize Small Sporty Van

Chúng ta có 6 loại xe ở đây. Hàm table cho biết có bao nhiêu xe ở mỗi loại:

table(Cars93$Type)

##
## Compact   Large Midsize   Small  Sporty     Van
##      16      11      22      21      14       9

prop.table chuyển đổi thành các tỉ lệ:

prop.table(table(Cars93$Type))

##
##    Compact      Large    Midsize      Small     Sporty        Van
## 0.17204301 0.11827957 0.23655914 0.22580645 0.15053763 0.09677419

Tương tự với xuất xứ của xe:

table(Cars93$Origin)

##
##     USA non-USA
##      48      45

prop.table(table(Cars93$Origin))

##
##      USA  non-USA
## 0.516129 0.483871

Cách tạo bảng chéo (contingency table)

Tuyệt vời, ta thấy bộ dữ liệu có số lượng xe Mỹ và không phải Mỹ gần tương đương và các loại phổ biến nhất là Midsize và Small. Tuy nhiên, có thể xe Mỹ và không phải Mỹ khác nhau về loại?

Hãy xem loại xe theo xuất xứ của chúng. Ta có thể dùng lại table, nhưng với hai đối số. Đối số thứ nhất sẽ thành biến hàng và đối số thứ hai sẽ thành biến cột:

table(Cars93$Type, Cars93$Origin)

##          
##           USA non-USA
##   Compact   7       9
##   Large    11       0
##   Midsize  10      12
##   Small     7      14
##   Sporty    8       6
##   Van       5       4

Bây giờ, ta thấy điều ai cũng biết: người Mỹ thích xe cỡ lớn! Bảng trên thể hiện phân phối đồng thời của hai biến phân loại (Type và Origin). Những bảng như vậy được gọi là bảng chéo.

Cách lấy tổng biên (marginals) từ bảng chéo

Hàm rowSums và colSums có ý nghĩa đúng như tên gọi

(tab1<-table(Cars93$Type, Cars93$Origin))

##          
##           USA non-USA
##   Compact   7       9
##   Large    11       0
##   Midsize  10      12
##   Small     7      14
##   Sporty    8       6
##   Van       5       4

rowSums(tab1)

## Compact   Large Midsize   Small  Sporty     Van
##      16      11      22      21      14       9

colSums(tab1)

##     USA non-USA
##      48      45

Cách tính phần trăm từ bảng chéo

prop.table lồng với table cho tần suất

prop.table(table(Cars93$Type, Cars93$Origin))

##          
##                  USA    non-USA
##   Compact 0.07526882 0.09677419
##   Large   0.11827957 0.00000000
##   Midsize 0.10752688 0.12903226
##   Small   0.07526882 0.15053763
##   Sporty  0.08602151 0.06451613
##   Van     0.05376344 0.04301075

Chuyển sang phần trăm chỉ là nhân với 100:

prop.table(table(Cars93$Type, Cars93$Origin))*100

##          
##                 USA   non-USA
##   Compact  7.526882  9.677419
##   Large   11.827957  0.000000
##   Midsize 10.752688 12.903226
##   Small    7.526882 15.053763
##   Sporty   8.602151  6.451613
##   Van      5.376344  4.301075

Lưu ý, đây là một phân phối xác suất đồng thời, từ đó ta có thể thấy, ví dụ, khoảng 7,5% số xe là loại nhỏ và có xuất xứ Mỹ.

Thường thì, ta quan tâm đến phân phối của một biến trong từng nhóm do một biến khác tạo ra. Ở đây, phân phối loại xe giữa nhóm xe Mỹ và (riêng) không phải Mỹ có vẻ thú vị. Để lấy điều này, ta dùng đối số margin của hàm prop.table. Nó cho biết biến nhóm nằm ở hàng (margin=1) hay ở cột (margin=2):

prop.table(table(Cars93$Type, Cars93$Origin), margin=2)*100

##          
##                 USA   non-USA
##   Compact 14.583333 20.000000
##   Large   22.916667  0.000000
##   Midsize 20.833333 26.666667
##   Small   14.583333 31.111111
##   Sporty  16.666667 13.333333
##   Van     10.416667  8.888889

Bây giờ ta dễ dàng thấy rằng xe nhỏ xuất hiện thường xuyên gấp đôi ở nhóm không phải Mỹ so với nhóm Mỹ trong bộ dữ liệu.

Cũng lưu ý rằng các phần trăm cộng lại thành 100 theo cột, trong khi ở bảng phân phối đồng thời (bảng không có đối số margin), 100 là tổng của cả bảng.

(tab2<-prop.table(table(Cars93$Type, Cars93$Origin), margin=2)*100)

##          
##                 USA   non-USA
##   Compact 14.583333 20.000000
##   Large   22.916667  0.000000
##   Midsize 20.833333 26.666667
##   Small   14.583333 31.111111
##   Sporty  16.666667 13.333333
##   Van     10.416667  8.888889

colSums(tab2)

##     USA non-USA
##     100     100

Kiểm định Chi-bình phương

Câu hỏi phổ biến nhất nảy sinh từ các bảng chéo là liệu các biến hàng và cột có độc lập với nhau không. Cách cơ bản nhất để trả lời là chạy kiểm định chi-bình phương. Chủ đề này được trình bày chi tiết trong hướng dẫn này. Hãy kiểm tra xem Type và Origin có độc lập không:

chisq.test(Cars93$Type, Cars93$Origin)

## Warning in chisq.test(Cars93$Type, Cars93$Origin): Chi-squared
## approximation may be incorrect

##
##  Pearson's Chi-squared test
##
## data:  Cars93$Type and Cars93$Origin
## X-squared = 14.08, df = 5, p-value = 0.01511

Rõ ràng là không, nhưng ta cũng nhận được cảnh báo Chi-squared approximation may be incorrect. Điều này là vì thống kê chi-bình phương chỉ xấp xỉ theo phân phối chi-bình phương. Số quan sát càng nhiều, xấp xỉ càng tốt. Hàm chisq.test đưa ra cảnh báo trên bất cứ khi nào một trong các tần số kỳ vọng nhỏ hơn 5 (về "tần số kỳ vọng" là gì, xem hướng dẫn liên kết ở trên).

Kiểm định chính xác Fisher

Kiểm định chính xác Fisher là một thay thế cho kiểm định chi-bình phương, chủ yếu dùng khi xấp xỉ chi-bình phương không thỏa đáng. Hãy chạy thử:

fisher.test(Cars93$Type, Cars93$Origin)

##
##  Fisher's Exact Test for Count Data
##
## data:  Cars93$Type and Cars93$Origin
## p-value = 0.007248
## alternative hypothesis: two.sided

Kết quả khá giống với chi-bình phương, nhưng không phải lúc nào cũng vậy. Một nhược điểm lớn của kiểm định Fisher là với các bảng lớn (hoặc mẫu lớn) nó trở nên kém hiệu quả về tính toán.

Kiểm định G

Một lựa chọn khác là kiểm định G. Thống kê của nó cũng xấp xỉ theo phân phối chi-bình phương, nhưng với mẫu nhỏ, xấp xỉ này gần hơn so với kiểm định chi-bình phương. Với kiểm định G, ta có thể dùng hàm GTest từ gói DescTools. Kết quả lại khá giống hai kiểm định trước: Type và Origin không độc lập.

library(DescTools)
GTest(Cars93$Type, Cars93$Origin)

##
##  Log likelihood ratio (G-test) test of independence without
##  correction
##
## data:  Cars93$Type and Cars93$Origin
## G = 18.362, X-squared df = 5, p-value = 0.002526

Hiệu chỉnh Yates

Với bảng chéo 2x2, kiểm định chi-bình phương có thể được cải thiện bằng hiệu chỉnh liên tục Yates. Cụ thể là trừ 0,5 khỏi mỗi hạng tử | Observed - Expected | trong thống kê chi-bình phương. Một lần nữa hãy tham khảo hướng dẫn này nếu bạn đang thấy bối rối. Hơn nữa, R tự động áp dụng hiệu chỉnh Yates khi cần. Hãy xem sự sẵn có của phiên bản hộp số tay ở xe Mỹ và không phải Mỹ:

(tab3<-table(Cars93$Man.trans.avail, Cars93$Origin))

##      
##       USA non-USA
##   No   26       6
##   Yes  22      39

chisq.test(tab3)

##
##  Pearson's Chi-squared test with Yates' continuity correction
##
## data:  tab3
## X-squared = 15.397, df = 1, p-value = 8.712e-05

Hiệu chỉnh Yates đã được áp dụng tự động, và R đã thông báo cho chúng ta.

Bảng 3 (hoặc nhiều) chiều

Hãy xem đồng thời Man.trans.avail, Origin và Type. table chia bộ dữ liệu theo biến cung cấp ở vị trí thứ ba:

table(Cars93$Man.trans.avail, Cars93$Origin, Cars93$Type)

## , ,  = Compact
##
##      
##       USA non-USA
##   No    2       0
##   Yes   5       9
##
## , ,  = Large
##
##      
##       USA non-USA
##   No   11       0
##   Yes   0       0
##
## , ,  = Midsize
##
##      
##       USA non-USA
##   No    9       4
##   Yes   1       8
##
## , ,  = Small
##
##      
##       USA non-USA
##   No    0       0
##   Yes   7      14
##
## , ,  = Sporty
##
##      
##       USA non-USA
##   No    0       0
##   Yes   8       6
##
## , ,  = Van
##
##      
##       USA non-USA
##   No    4       2
##   Yes   1       2

ftable cho cái nhìn cô đọng hơn:

ftable(Cars93$Man.trans.avail, Cars93$Origin, Cars93$Type)

##              Compact Large Midsize Small Sporty Van
##                                                    
## No  USA            2    11       9     0      0   4
##     non-USA        0     0       4     0      0   2
## Yes USA            5     0       1     7      8   1
##     non-USA        9     0       8    14      6   2

Kiểm định Cochran–Mantel–Haenszel

Từ kết quả table ở trên, có thể thấy mối quan hệ giữa Origin và Man.trans.avail khác nhau theo Type. Với xe nhỏ và xe thể thao, chẳng hạn, không có liên hệ: mọi xe nhỏ hoặc thể thao, bất kể Mỹ hay không phải Mỹ, đều có bản số tay. Mặt khác, đa số xe Midsize của Mỹ không có số tay, trong khi đa số xe không phải Mỹ thuộc loại này lại có. Để xét đến các mối quan hệ có thể khác nhau giữa các tầng (strata), có thể dùng kiểm định Cochran–Mantel–Haenszel:

mantelhaen.test(Cars93$Man.trans.avail, Cars93$Origin, Cars93$Type)

##
##  Mantel-Haenszel chi-squared test with continuity correction
##
## data:  Cars93$Man.trans.avail and Cars93$Origin and Cars93$Type
## Mantel-Haenszel X-squared = 8.0153, df = 1, p-value = 0.004638
## alternative hypothesis: true common odds ratio is not equal to 1
## 95 percent confidence interval:
##   2.226531 76.891307
## sample estimates:
## common odds ratio
##          13.08438

Đối số thứ ba truyền vào mantelhaen.test xác định các tầng. Hãy so sánh kết quả trên với kết quả không phân tầng (ví dụ hiệu chỉnh Yates ở trên). Vẫn có liên hệ, nhưng bằng chứng yếu hơn nhiều.

Các thước đo liên hệ

Sau khi phát hiện một số liên hệ giữa các biến, đã đến lúc đo lường độ mạnh của chúng. Có rất nhiều loại thước đo khác nhau. Nhiều trong số đó được mô tả tại đây. Ở đây tôi sẽ tập trung vào hai thước đo được dùng rộng rãi nhất.

V của Cramer

V được xây dựng từ thống kê chi-bình phương:

$$ V = \sqrt{\frac{\chi^2/N}{\min(C-1, R-1)}}, $$ trong đó:

  • N là tổng chung của bảng chéo (tổng của tất cả ô),
  • C là số cột
  • R là số hàng.

V ∈ [0; 1]. V càng lớn, mối quan hệ giữa các biến càng mạnh. Có thể diễn giải V = 0 là độc lập (vì V = 0 khi và chỉ khi χ2 = 0). Nhược điểm chính của V là thiếu diễn giải định lượng rõ ràng. V = 0,6 là liên hệ mạnh, trung bình hay yếu?

Hàm CramerV từ DescTools có thể tính giúp chúng ta:

CramerV(Cars93$Type, Cars93$Origin)

## [1] 0.3890967

Một lần nữa: đây là liên hệ mạnh, trung bình, hay yếu?

Lambda của Goodman và Kruskal

Lambda của Goodman và Kruskal là ví dụ về thước đo dựa trên mức giảm tỉ lệ biến thiên. Các thước đo dạng này nhằm mô phỏng R2 - hệ số xác định trong hồi quy tuyến tính:

  • chúng nhận giá trị trong [0, 1]
  • chúng biểu đạt phần biến thiên được biến độc lập giải thích.

Hãy dùng biến cột làm biến độc lập. Khi đó công thức lambda của Goodman và Kruskal là: $$\lambda = \frac{L - \sum_j L_j}{L},$$ trong đó:

  • Lj là tổng tần suất không thuộc mốt ở cột thứ j,
  • L là tổng tần suất không thuộc mốt ở cột "tổng"

Giải thích rõ nhất bằng ví dụ. Xem Type và Origin, trong đó biến sau là biến độc lập:

table(Cars93$Type, Cars93$Origin)

##          
##           USA non-USA
##   Compact   7       9
##   Large    11       0
##   Midsize  10      12
##   Small     7      14
##   Sporty    8       6
##   Van       5       4

Mốt của cột USA là 11, nên L1 = 7 + 10 + 7 + 8 + 5 = 37. Mốt của cột không phải Mỹ là 14, nên L2 = 9 + 0 + 12 + 6 + 4 = 31. Cột "tổng" là

rowSums(table(Cars93$Type, Cars93$Origin))

## Compact   Large Midsize   Small  Sporty     Van
##      16      11      22      21      14       9

Mốt là 22, vậy L = 16 + 11 + 21 + 14 + 9 = 71 và $$\lambda = \frac{71 - (37+31)}{71}=0.042$$

Ta có thể dùng hàm Lambda từ gói DescTools thay thế:

Lambda(Cars93$Type, Cars93$Origin, direction='row')

## [1] 0.04225352

Tham số direction xác định biến phụ thuộc nằm ở đâu (row hay column).

Origin chỉ giải thích khoảng 4% biến thiên của Type. Lưu ý từ công thức rằng lambda định nghĩa biến thiên như một nhị phân giữa thuộc và không thuộc nhóm lớn nhất.

Đáng chú ý là lambda bằng 0 bất cứ khi nào hạng mục mốt của mỗi cột đều giống nhau. Xem bảng này:

(lambdaTab<-cbind(c(0,100), c(49,51)))

##      [,1] [,2]
## [1,]    0   49
## [2,]  100   51

chisq.test(lambdaTab)

##
##  Pearson's Chi-squared test with Yates' continuity correction
##
## data:  lambdaTab
## X-squared = 62.279, df = 1, p-value = 2.981e-15

Hạng mục mốt giống nhau cho mỗi cột (là hàng thứ hai). Vì vậy lambda phải bằng 0 mặc dù có liên hệ đáng kể và dễ thấy:

Lambda(lambdaTab, direction='row')

## [1] 0

Nếu bạn muốn học thêm về thống kê trong R, hãy tham gia khóa Statistical Modeling in R (Phần 1) của DataCamp.

Chủ đề

Liên quan