跳至主要内容

R 列联表教程

在本教程中,您将学习如何创建列联表,以及如何检验并量化其中可见的关系。
· 10分钟 阅读

使用 AI 探索

如何制作表格

首先,让我们获取一些数据。MASS 包含关于 1993 年在美国销售的 93 款汽车的数据。它们存储在 Cars93 对象中,每辆车包含 27 个特征,其中一些为分类变量。我们先加载 MASS 包,并查看 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

这里有 6 种类型的汽车。table 函数可以告诉我们各类型的数量:

table(Cars93$Type)

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

prop.table 将其转换为比例:

prop.table(table(Cars93$Type))

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

对汽车来源也做同样的处理:

table(Cars93$Origin)

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

prop.table(table(Cars93$Origin))

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

如何制作列联表

很好,我们看到数据集中美国产与非美国产汽车数量相近,且最常见的类型是 Midsize 和 Small。不过,美国产和非美国产在类型上是否存在差异?

我们来按来源查看汽车类型。仍然使用 table,但这次传入两个参数。第一个成为行变量,第二个成为列变量:

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

现在,我们看到了众所周知的事实:美国人偏爱大型车!上表显示了两个分类变量(Type 和 Origin)的联合分布。此类表格称为列联表。

如何从列联表获取边际分布

rowSums 和 colSums 函数的作用一目了然

(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

如何从列联表获取百分比

prop.table 与 table 嵌套可得到频率

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

转换为百分比只需乘以 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

注意,这是一种联合概率分布,我们可以看到,例如,约 7.5% 的汽车是小型且来源于美国。

更常见的是,我们关心在由另一个变量划分的组内,一个变量的分布。例如,分别在美国产与非美国产汽车中,车型的分布看起来很有意思。为此,我们在 prop.table 中使用 margin 参数。它指定分组变量位于行(margin=1)还是列(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

现在我们可以清楚地看到,在非美国产样本中,小型车的占比是美国产的两倍。

还要注意,带有 margin 参数的表格中,各列的百分比之和为 100;而在联合分布表(不带 margin 参数)中,整张表的总和为 100。

(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

卡方检验

基于列联表最常见的问题是行变量与列变量是否相互独立。最基本的做法是运行卡方检验。这篇教程有详细介绍。我们来检验 Type 与 Origin 是否独立:

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

显然,它们并不独立,但我们还收到了 Chi-squared approximation may be incorrect 的警告。这是因为卡方统计量仅近似服从卡方分布。样本量越大,近似越好。每当某个期望计数小于 5 时,chisq.test 就会抛出上述警告(关于“期望计数”的含义,请参阅上面链接的教程)。

Fisher 精确检验

Fisher 精确检验是卡方检验的替代方法,主要用于卡方近似不理想的情况。我们来运行它:

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

结果与卡方检验相当接近,但并非总是如此。Fisher 精确检验的一个主要缺点是:对于较大的表(或较大的样本),其计算效率较低。

G 检验

另一个替代方法是所谓的 G 检验。其统计量也近似服从卡方分布,但对于小样本,这种近似通常比卡方检验更好。进行 G 检验可以使用 DescTools 包中的 GTest 函数。结果同样与前两个检验相近:Type 与 Origin 并不独立。

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

Yates 连续性校正

在 2x2 列联表中,卡方检验可配合 Yates 连续性校正。它只是将卡方统计量中的每个 |Observed - Expected| 项减去 0.5。如果此处您有些迷惑,请再次参考该教程。另外需要说明的是,R 在必要时会自动应用 Yates 校正。我们来查看美国产与非美国产汽车是否提供手动变速箱版本:

(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

Yates 校正已自动应用,且 R 给出了提示。

三维(或更高维)表

我们同时查看 Man.trans.avail、Origin 和 Type。table 会按第三个变量对数据集进行拆分:

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 提供更紧凑的视图:

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

Cochran–Mantel–Haenszel 检验

从上面的 table 结果可以看出,Origin 与 Man.trans.avail 之间的关系会随 Type 而变化。比如对小型车和运动型车来说并无关联:无论美国产还是非美国产,小型或运动型车都提供手动版本。另一方面,大多数美国产中型车没有手动版本,而大多数同类非美国产车则有。为考虑各分层中可能不同的关系,可使用 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

传递给 mantelhaen.test 的第三个参数用于标识分层。将上述结果与未分层的结果(上文 Yates 校正示例)进行比较。关联依然存在,但证据明显更弱。

关联度量

在发现变量之间存在某些关联后,接下来需要衡量其强度。可选的度量方法非常多,其中许多在此处有所描述。下面我将重点介绍两种最常用的方法。

Cramer 的 V

V 基于卡方统计量:

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

  • N 为列联表的总计(所有单元格之和),
  • C 为列数,
  • R 为行数。

V ∈ [0, 1]。V 越大,变量之间的关系越强。V = 0 可解释为独立(因为当且仅当 χ2 = 0 时 V = 0)。V 的主要缺点是缺乏精确的解释标准。V = 0.6 到底算强、中等还是弱关联?

DescTools 包中的 CramerV 函数可以为我们计算:

CramerV(Cars93$Type, Cars93$Origin)

## [1] 0.3890967

再问一次:这是强关联、中等关联,还是弱关联?

Goodman 与 Kruskal 的 lambda

Goodman 与 Kruskal 的 lambda 是一种基于比例变差缩减(PRV)的度量。这类度量旨在模拟线性回归中的决定系数 R2:

  • 取值范围为 [0, 1]
  • 表示由自变量解释的变差比例。

我们以列变量为自变量。此时 Goodman 与 Kruskal 的 lambda 公式为:$$\lambda = \frac{L - \sum_j L_j}{L},$$ 其中:

  • Lj 为第 j 列的非众数频数之和,
  • L 为“总计”列中的非众数频数之和

用例子最容易说明。我们来看 Type 与 Origin,后者作为自变量:

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

美国产(USA)列的众数为 11,因此 L1 = 7 + 10 + 7 + 8 + 5 = 37。非美国产(non-USA)列的众数为 14,因此 L2 = 9 + 0 + 12 + 6 + 4 = 31。“总计”列为

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

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

众数为 22,所以 L = 16 + 11 + 21 + 14 + 9 = 71,且 $$\lambda = \frac{71 - (37+31)}{71}=0.042$$

我们也可以使用 DescTools 包的 Lambda 函数:

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

## [1] 0.04225352

direction 参数用于指定因变量位于 row 还是 column。

Origin 仅能解释 Type 中约 4% 的变差。注意从公式可见,lambda 将变差定义为“是否属于最大组别”的二分法。

值得一提的是,只要每一列的众数类别相同,lambda 就为零。看下面这张表:

(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

每一列的众数类别相同(都是第二行)。因此,尽管关联显著且肉眼可见,lambda 仍必须为零:

Lambda(lambdaTab, direction='row')

## [1] 0

如果您希望进一步学习 R 中的统计学,请参加 DataCamp 的 Statistical Modeling in R (Part 1) 课程。

主题