实现的方法:
1、SPSS中有Case-control matching(个案控制匹配)的方法可以实现该抽样,具体可参考如下文章:
SPSS:病例-对照匹配(case-control?matching)的实现过程?|?Public?Library?of?Bioinformatics
2、用Python实现对照匹配抽样的方法,参考了这篇文章中SAS程序的思路,用Python实现。
病例对照、匹配(配对)抽样?SAS?程序(原创)?-?SAS专版?-?经管之家(原人大经济论坛)
测试数据如下:
数据表包含个案编号ID,需要匹配的变量?shcool、grade、class和age,分组变量group(1是实验组,0是对照组)。
ID | school | grade | class | name | age | group |
1 | AA | 1 | 1 | 马莺琼 | 19 | 1 |
2 | AA | 1 | 1 | 范瑞娜 | 17 | 0 |
3 | AA | 1 | 1 | 陆艺 | 19 | 0 |
4 | AA | 1 | 1 | 韩薇婕 | 18 | 0 |
5 | AA | 1 | 2 | 黎舒 | 16 | 1 |
6 | AA | 1 | 2 | 江月菁 | 19 | 0 |
7 | AA | 1 | 2 | 詹枝静 | 18 | 0 |
8 | AA | 1 | 2 | 朱炎力 | 18 | 0 |
9 | AA | 2 | 1 | 游盛 | 16 | 1 |
10 | AA | 2 | 1 | 翁钧行 | 18 | 0 |
11 | AA | 2 | 1 | 潘策子 | 19 | 0 |
12 | AA | 2 | 1 | 姜富 | 19 | 0 |
13 | AA | 2 | 2 | 丁祥明 | 16 | 1 |
14 | AA | 2 | 2 | 柯龙 | 16 | 0 |
15 | AA | 2 | 2 | 钟永国 | 17 | 0 |
16 | AA | 2 | 2 | 卢巧 | 19 | 0 |
17 | BB | 1 | 1 | 孙俊龙 | 16 | 1 |
18 | BB | 1 | 1 | 童安伟 | 16 | 0 |
19 | BB | 1 | 1 | 沈强 | 16 | 0 |
20 | BB | 1 | 1 | 马致 | 20 | 0 |
21 | BB | 1 | 2 | 沈家康 | 16 | 1 |
22 | BB | 1 | 2 | 江弘 | 20 | 0 |
23 | BB | 1 | 2 | 阮建 | 18 | 0 |
24 | BB | 1 | 2 | 邵强 | 19 | 0 |
25 | BB | 2 | 1 | 黃巧 | 19 | 1 |
26 | BB | 2 | 1 | 罗影琦 | 20 | 0 |
27 | BB | 2 | 1 | 卓姬 | 16 | 0 |
28 | BB | 2 | 1 | 温姬娜 | 17 | 0 |
29 | BB | 2 | 2 | 庄倩 | 16 | 1 |
30 | BB | 2 | 2 | 曾悦 | 16 | 0 |
31 | BB | 2 | 2 | 汪娥莲 | 18 | 0 |
32 | BB | 2 | 2 | 欧丽茗 | 18 | 0 |
需求:
(1)需要对数据做抽样,要求对实验组中的每一个案例,从对照组中随机抽样出school、grade、class相同,age相差不超过2岁的案例。
(2)进行不放回抽样,也就是说,每一个已经被抽样出来的对照组案例,在后续的抽样中都不会再被抽到。
思路:
(1)拆分实验组和对照组的数据
(2)对实验组中的每个个案,根据school、grade、class、age匹配,找到符合条件的所有对照组个案,实现匹配
(3)从符合条件的所有对照组个案中,随机抽取一个,实现随机抽样
(4)将已抽取出来的对照组个案从对照组数据中删掉,实现不放回抽样
(5)循环处理所有的实验组个案
代码:
import?pandas?as?pd
from?random?import?choice
#?读取数据
file_path?= './抽样测试数据.xlsx'
data?=?pd.read_excel(file_path)
#?显示前几行数据
data.head()
#?将数据拆分为实验组?(group=1)?和对照组?(group=0)
experimental_group?=?data[data['group'] == 1]
control_group?=?data[data['group'] == 0]
#?定义函数,对某一个特定的实验组个案,根据school、grade、class进行匹配,并确保age相差不超过2岁,筛选符合条件的控制组个案(0到多个)
def find_matching_cases(exp_case,?control_group):
????matching_cases?=?control_group[
(control_group['school'] ==?exp_case['school']) &
(control_group['grade'] ==?exp_case['grade']) &
(control_group['class'] ==?exp_case['class']) &
(control_group['age'].between(exp_case['age'] - 2,?exp_case['age'] + 2))
]
return?matching_cases
#?列表存储已经抽取出来的控制组个案的ID,确保它不会再次被抽取
selected_control_indices?= []
#?对实验组中的每一个个案,找到一个匹配的控制组个案
matched_samples?= []
for?_,?exp_case?in?experimental_group.iterrows():
#?调用前面的函数,筛选符合条件的控制组个案
????matching_control_cases?=?find_matching_cases(exp_case,?control_group)
#?从符合条件的控制组个案中,移除已经抽取过的个案
????matching_control_cases?=?matching_control_cases[~matching_control_cases.index.isin(selected_control_indices)]
if not?matching_control_cases.empty:
#?在符合条件的控制组个案中,随机选一个出来
????????selected_control_case?=?choice(matching_control_cases.to_dict('records'))
????????matched_samples.append(selected_control_case)
#?将抽取出的个案的ID加入列表
????????selected_control_indices.append(selected_control_case['ID'])
#?把匹配样本输出到?DataFrame?中
matched_samples_df?=?pd.DataFrame(matched_samples)
#?显示前几行数据
matched_samples_df.head()
实际业务应用场景中,将数据源和字段名替换即可。