说明
1. 译自Norbert Köhler的文章How to use R for matching samples (propensity score)
根据维基百科,倾向性评分匹配(propensity score matching,PSM)是一种用来评估处置效应的统计方法。
Propensity score matching (PSM) is a statistical matching technique that attempts to estimate the effect of a treatment, policy, or other intervention by accounting for the covariates that predict receiving the treatment
广义说来,倾向性评分分析假设只有当两组样本的研究对象具有相似特征时才能进行两组样本之间的无偏比较(unbiased comparison)。因此,PSM不仅仅是随机试验的一种替代方法,它也是流行病研究中进行样本比较的重要方法之一。让我们举个例子:
与健康相关的生活质量(Health-related quality of life,HRQOL)被认为是癌症治疗的重要结果之一。对癌症患者而言,最常用的HRQOL测度是通过欧洲癌症研究与治疗中心(European Organisation for Research and Treatment of Cancer,EORTC)的调查问卷计算得出的。
EORTC QLD-C30是一个由30个项目组成,包括5个功能量表,9个症状量表和一个整体生活质量量表的的问卷。所有量表都会给出一个0-100之间的得分。症状量表得分越高代表被调查人症状越重,其余两个量表得分越高代表生活质量越高。
然而,如果没有任何参照,直接对数据进行解释是很困难的。幸运的是,EORTC QLQ-C30问卷也在一些正常人群调查中使用,我们可以对比患者的得分和正常人群的得分差异,从而判断患者的一些症状和功能障碍是否能归因于癌症(或治疗)。PSM可以通过相似年龄和相同性别等特征,将患者和一般人群进行匹配。
生成两个随机数据框
本文用到的所有包可通过如下代码加载:
1 | pacman::p_load(knitr, wakefield, MatchIt, tableone, captioner) |
由于我们不希望在博客文章中使用真实数据,因此需要生成一些模拟数据。使用wakefield
包可以很容易地实现这个功能。
创建一个名为
df.patients
的数据框,包含250个病人的年龄和性别数据,所有病人的年龄都要在30-78岁之间,并且70%的病人被设定为男性。1
2
3
4
5
6
7set.seed(180717)
library(wakefield)
df.patients <- r_data_frame(n = 250,
age(x = 30:78, name = 'Age'),
sex(x = c("Male", "Female"),
prob = c(0.70, 0.30), name = "Sex"))
df.patients$Sample <- as.factor('Patients')summary函数
会返回创建的数据框的基本信息。患者平均年龄为53.7岁,并且大约70%为男性(66.8%)。1
2
3
4
5
6
7
8
9summary(df.patients)
Age Sex Sample
Min. :30.00 Male :167 Patients:250
1st Qu.:44.00 Female: 83
Median :57.00
Mean :55.24
3rd Qu.:67.00
Max. :78.00接下来,需要创建另一个名为
df.population
的数据框。我们希望这个数据集的数据和患者df.patients
有些不同,因此正常人群的年龄区间被设定为18-80岁,并且男女各占一半。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20set.seed(180717)
df.population <- r_data_frame(n = 1000, age(x = 18:80, name = 'Age'),
sex(x = c("Male", "Female"),
prob = c(0.50, 0.50),
name = "Sex"))
df.population$Sample <- as.factor('Population')
#下方表格显示样本平均年龄为50.1岁,男女比例也大致相等。
summary(df.population)
Age Sex Sample
Min. :18.0 Male :489 Population:1000
1st Qu.:34.0 Female:511
Median :51.0
Mean :50.1
3rd Qu.:66.0
Max. :80.0
合并数据框
在匹配样本之前,我们需要把两个数据框合并。首先,生成一个新变量Group
来代表研究对象来自哪个全体(逻辑型变量),再添加另一个变量Distress
来表示个体的痛苦程度。Distress
变量是利用wakefield包
中的age函数
创建的,可以发现,女性承受的痛苦级别更高。
1 | mydata <- rbind(df.patients, df.population) |
if..else语句的基本语法是
1 | if(boolean_expression) { |
当我们比较两组样本的年龄和性别分布时,我们可以发现明显的区别:
1 | library(pacman) |
Table 1: Comparison of unmatched samples
Patients | Population | p | |
---|---|---|---|
n | 250 | 1000 | |
Age (mean (sd)) | 55.24 (14.24) | 50.10 (18.37) | <0.001 |
Sex = Female (%) | 83 (33.2) | 511 (51.1) | <0.001 |
Distress (mean (sd)) | 25.12 (11.58) | 24.35 (11.34) | 0.344 |
样本匹配
现在,我们已经完成了全部的准备工作,可以开始使用MatchIT包
中的matchit函数
来匹配两组样本了。
1 | library(MatchIt) |
- 函数中
method=‘nearest’
的设定指明了使用近邻法(the nearest neighbors method)进行匹配。 - 其他方法包括exact matching,subclassification,optimal
matching,genetic matching,ull
matching等(
method = c("exact", "subclass", "optimal", ""genetic", "full")
)。 ratio=1
意味着这是一一配对。在我们的例子里,对于患者组中的每一个病例,将恰好匹配正常人群样本中的一个人。- 同时也请注意
Group变量
需要是逻辑型变量(TRUE与FALSE)。
为了后续工作的便利,我们将summary函数
的输出赋值给名为a
的变量。在匹配完样本后,正常人群样本量减少到了和患者样本一致(n=250,详见Table
2)。
1 | kable(a$nn, digits = 2, align = 'c', caption = 'Table 2: Sample sizes') |
Table 2: Sample sizes
Control | Treated | |
---|---|---|
All | 1000 | 250 |
Matched | 250 | 250 |
Unmatched | 750 | 0 |
Discarded | 0 | 0 |
根据输出结果,匹配后的年龄和性别分布基本一致了。
1 | kable(a$sum.matched[c(1,2,4)], digits = 2, align = 'c', caption = 'Table 3: Summary of balance for matched data') |
Table 3: Summary of balance for matched data
Means Treated | Means Control | Mean Diff | |
---|---|---|---|
distance | 0.23 | 0.23 | 0.00 |
Age | 55.24 | 55.22 | 0.02 |
SexMale | 0.67 | 0.67 | 0.00 |
SexFemale | 0.33 | 0.33 | 0.00 |
倾向性评分的分布可以使用MatchIt包
中的plot函数
进行绘制。
1 | plot(match.it, type = 'jitter', interactive = FALSE) |
保存匹配样本
最后,匹配的样本将保存到名为df.match
的新数据框中。
1 | # 获得匹配数据集(含distance和weights列)的第1~ncol(mydata)列 |
Patients | Population | p | |
---|---|---|---|
n | 250 | 250 | |
Age (mean (sd)) | 55.24 (14.24) | 55.22 (14.21) | 0.987 |
Sex = Female (%) | 83 (33.2) | 83 (33.2) | 1.000 |
Distress (mean (sd)) | 25.12 (11.58) | 22.87 (11.72) | 0.032 |