PSM倾向得分匹配法【python实操篇】

article/2025/6/29 20:39:37

前言

大家好,我是顾先生,PSM倾向性得分匹配法的Python代码实操终于来啦!

对于PSM原理不太熟悉的同学可以看看前一篇文章:PSM倾向得分匹配法【上篇:理论篇】

目前网上PSM实操的相关文章都是R语言、SPSS和STATA实现的,少数Python版本代码不全,可读性有限(有些甚至要钱。。。)

所以我想出一版可读性强、能迅速复用的Python版本PSM,让各位同学既能看懂又能快速上手。

本次Python代码实操主要参考了psmatching的源码,并做了一定的修改,地址在文末参考资料。

这次我把每段代码都做了保姆级的注释,相信每位同学都能理解到位,当然肯定有注释不对的地方,也欢迎后台私信我。

本文的代码和数据集可关注我的公众号“顾先生聊数据”,后台发送“psm”后领取~

数据集介绍

为了更好地进行演示,我现编了一个数据集,该数据集以电商场景为基础,判断给用户发送优惠券PUSH是否会影响到用户。
在这里插入图片描述

数据集的类别主要由事实层标签(年龄、性别等)和行为层标签(最近一次购买diff、之前使用优惠券情况等)。

重申一下!数据集是我randbetween现编的,不用太较真具体内容。

完整代码

下面的代码我做了尽可能详细的注释,复用时需要修改的地方我也做了标注,如有不合理的地方欢迎后台私聊我哦~

安装psmatching包。

!pip install psmatching
import psmatching.match as psm
import pytest
import pandas as pd
import numpy as np
from psmatching.utilities import *
import statsmodels.api as sm

path及model设置。

#地址
path = "./data/psm/psm_gxslsj_data.csv"
#model由干预项和其他类别标签组成,形式为"干预项~类别特征+列别特征。。。"
model = "PUSH ~ AGE + SEX + VIP_LEVEL + LASTDAY_BUY_DIFF + PREFER_TYPE + LOGTIME_PREFER + USE_COUPON_BEFORE + ACTIVE_LEVEL"
#想要几个匹配项,如k=3,那一个push=1的用户就会匹配三个push=0的近似用户
k = "3"
m = psm.PSMatch(path, model, k)

获得倾向性匹配得分。

df = pd.read_csv(path)
#将用户ID作为数据的新索引
df = df.set_index("ID")
print("\n计算倾向性匹配得分 ...", end = " ")
#利用逻辑回归框架计算倾向得分,即广义线性估计 + 二项式Binomial
glm_binom = sm.formula.glm(formula = model, data = df, family = sm.families.Binomial())
#拟合拟合给定family的广义线性模型
#https://www.w3cschool.cn/doc_statsmodels/statsmodels-generated-statsmodels-genmod-generalized_linear_model-glm-fit.html?lang=en
result = glm_binom.fit()
# 输出回归分析的摘要
# print(result.summary)
propensity_scores = result.fittedvalues
print("\n计算完成!")
#将倾向性匹配得分写入data
df["PROPENSITY"] = propensity_scores

计算倾向性匹配得分 …
计算完成!

groups是干预项,propensity是倾向性匹配得分,这里要分开干预与非干预,且确保n1<n2。
注意:将PUSH替换成自己的干预项。

groups = df.PUSH
propensity = df.PROPENSITY
#把干预项替换成True和False
groups = groups == groups.unique()[1]
n = len(groups)
#计算True和False的数量
n1 = groups[groups==1].sum()
n2 = n-n1
g1, g2 = propensity[groups==1], propensity[groups==0]
#确保n2>n1,,少的匹配多的,否则交换下
if n1 > n2:n1, n2, g1, g2 = n2, n1, g2, g1
随机排序干预组,减少原始排序的影响
m_order = list(np.random.permutation(groups[groups==1].index))

根据倾向评分差异将干预组与对照组进行匹配
注意:caliper = None可以替换成自己想要的精度

matches = {}
k = int(k)
print("\n给每个干预组匹配 [" + str(k) + "] 个对照组 ... ", end = " ")
for m in m_order:# 计算所有倾向得分差异,这里用了最粗暴的绝对值# 将propensity[groups==1]分别拿出来,每一个都与所有的propensity[groups==0]相减dist = abs(g1[m]-g2)array = np.array(dist)#如果无放回地匹配,最后会出现要选取3个匹配对象,但是只有一个候选对照组的错误,故进行判断if k < len(array):# 在array里面选择K个最小的数字,并转换成列表k_smallest = np.partition(array, k)[:k].tolist()# 用卡尺做判断caliper = Noneif caliper:caliper = float(caliper)# 判断k_smallest是否在定义的卡尺范围keep_diffs = [i for i in k_smallest if i <= caliper]keep_ids = np.array(dist[dist.isin(keep_diffs)].index)else:# 如果不用标尺判断,那就直接上k_smallest了keep_ids = np.array(dist[dist.isin(k_smallest)].index)#  如果keep_ids比要匹配的数量多,那随机选择下,如要少,通过补NA配平数量if len(keep_ids) > k:matches[m] = list(np.random.choice(keep_ids, k, replace=False))elif len(keep_ids) < k:while len(matches[m]) <= k:matches[m].append("NA")else:matches[m] = keep_ids.tolist()# 判断 replace 是否放回replace = Falseif not replace:g2 = g2.drop(matches[m])
print("\n匹配完成!")

给每个干预组匹配 [3] 个对照组 …
匹配完成!

这里提一下抽取规则:
抽取规则分无放回匹配有放回匹配两种。如对照组个体不多,可选择有放回匹配,但重复选择对照组的样本进行匹配,会降低最终匹配样本的样本量,估计精度下降。大家可依据自身样本情况修改代码。

再提一下对应关系:
对应关系分一对一一对多两种。一对一匹配样本少,估计方差较大,且每个匹配都是最近的,故偏差小; 一对多匹配样本多,估计精度高,但由于干预组个体匹配多个,故相似度降低,偏差会增加。本文使用的是一对多,大家可依据自身样本情况修改代码。

将匹配完成的结果合并起来

matches = pd.DataFrame.from_dict(matches, orient="index")
matches = matches.reset_index()
column_names = {}
column_names["index"] = "干预组"
for i in range(k):column_names[i] = str("匹配对照组_" + str(i+1))
matches = matches.rename(columns = column_names)

从全部data中提取干预组和匹配对照的数据。
这里直接调用get_matched_data,注意输入的matches是匹配结果,df是全部数据。

matched_data = get_matched_data(matches, df)

将结果数据写入到文档
注意:可以自己更改地址

print("将倾向性匹配得分写入到文档 ...", end = " ")
save_file = path.split(".")[0] + "_倾向性匹配得分.csv"
df.to_csv(save_file, index = False)
print("完成!")
print("将匹配结果写入到文档 ...", end = " ")
save_file = path.split(".")[0] + "_匹配结果.csv"
matches.to_csv(save_file, index = False)
print("完成!")

将倾向性匹配得分写入到文档 … 完成!
将匹配结果写入到文档 … 完成!

对匹配结果进行变量检验
#注意:将PUSH替换成自己的干预项

variables = df.columns.tolist()[0:-2]
results = {}
print("开始评估匹配 ...")
#注意:将PUSH替换成自己的干预项
for var in variables:# 制作用于卡方检验的频率计数交叉表crosstable = pd.crosstab(df['PUSH'],df[var])if len(df[var].unique().tolist()) <= 2:# 计算 2x2 表的卡方统计量、df 和 p 值p_val = calc_chi2_2x2(crosstable)[1]else:# 计算 2x2 表的卡方统计量、df 和 p 值p_val = calc_chi2_2xC(crosstable)[1]results[var] = p_valprint("\t" + var + '(' + str(p_val) + ')', end = "")if p_val < 0.05:print(": 未通过")else:print(": 通过")
if True in [i < 0.05 for i in results.values()]:print("\n变量未全部通过匹配")
else:print("\n变量全部通过匹配")

开始评估匹配 … AGE(0.9904): 通过
SEX(0.6688): 通过
VIP_LEVEL(0.0089): 未通过
LASTDAY_BUY_DIFF(0.5134): 通过
PREFER_TYPE(0.7107): 通过
LOGTIME_PREFER(0.2442): 通过
USE_COUPON_BEFORE(0.2961): 通过
ACTIVE_LEVEL(0.7934): 通过
变量未全部通过匹配

这里提一句,为啥P值<0.05未通过,P值>0.05反而通过呢?

这是因为这里的"通过"与假设检验里面的"通过"不太一样。
我们做PSM,是为了在对照组中找到与干预组类似的个体。
那单个个体中的各个变量,应该和干预项"PUSH"是相互独立的。

所以上面的检验中,VIP_LEVEL(0.0089)显示未通过。

后记

ok,PSM的python实操就介绍到这里了,相信看到这里的同学肯定已经学会了如何使用PSM了吧~

本文的代码和数据集可关注我的公众号“顾先生聊数据”,后台发送“psm”后领取~
如有不清楚的地方,也欢迎后台私信我一起讨论。

参考资料:
⼩洛学习群——因果推断(第⼀期)
陈强 高级计量经济学及stata应用(第二版)
https://github.com/rickydangc/psmatching
https://mattzheng.blog.csdn.net/
https://www.jianshu.com/p/34dd19ebe475


http://chatgpt.dhexx.cn/article/MUf6KOwc.shtml

相关文章

数据分析36计(九):倾向得分匹配法(PSM)量化评估效果分析

1. 因果推断介绍 如今量化策略实施的效果评估变得越来越重要&#xff0c;数据驱动产品和运营、业务等各方的理念越来越受到重视。如今这方面流行的方法除了实验方法AB testing外&#xff0c;就是因果推断中的各种观察研究方法。 “统计相关性并不意味着因果关系”&#xff0c;数…

PSM倾向得分匹配

1. 简要介绍 我们以 是否上大学 () 对 收入 () 的影响为例来说明这个问题。这里&#xff0c;先讲二者的关系设定为如下线性模型&#xff1a; 显然&#xff0c;在模型 (1) 的设定中&#xff0c;我们可能忽略了一些同时影响「解释变量」—— 是否上大学 () 和「被解释变量」——…

stata 倾向得分匹配操作

倾向得分匹配法是一种研究方法&#xff0c;它在研究某项治疗、政策、或者其他事件的影响因素上很常见。对于经济、金融学领域来说&#xff0c;比如需要研究某个劳动者接受某种高等教育对其收入的影响&#xff0c;或者比如研究某个企业运用了某项管理层激励措施以后对企业业绩的…

倾向得分匹配(PSM)的原理以及应用

该文章主要介绍倾向得分匹配&#xff08;PSM, Propensity Score Matching&#xff09;方法的原理以及实现。这是一种理论稍微复杂、但实现较为容易的分析方法&#xff0c;适合非算法同学的使用。可用于&#xff08;基于观察数据的&#xff09;AB实验、增量模型搭建等领域。 文章…

倾向得分匹配只看这篇就够了

一、倾向得分匹配法说明 倾向得分匹配模型是由Rosenbaum和Rubin在1983年提出的&#xff0c;首次运用在生物医药领域&#xff0c;后来被广泛运用在药物治疗、计量研究、政策实施评价等领域。倾向得分匹配模型主要用来解决非处理因素&#xff08;干扰因素&#xff09;的偏差。 …

Android设置图标背景透明

这里写自定义目录标题 Android 设置图标背景透明速览引言调整背景色 Android 设置图标背景透明 速览 设置 android:background"#00ffffff" 引言 适用于 Vector Assets 和 透明背景的图片 想要在Android中使用透明背景的图片 首先得保证图片本身是透明背景的 不然也没…

如何设置背景透明度

设置背景透明度分为两种&#xff1a;一种背景为颜色设置的纯色背景&#xff1b;另一种是图片做背景。 【情况一】纯色背景 关键代码&#xff1a;background:rgba(R,G,B,A) RGB--------三原色&#xff08;red,green,bule&#xff09;A-------透明度 关于三原色最终成型的颜色…

html悬浮背景透明视频教程,在html中使用背景透明的video视频

由于对效果的要求&#xff0c;需要加入透明背景的video。经过了解&#xff0c;现代浏览器(新版 Chrome、Firefox、Safari 等)已经全面支持 webM 格式的视频了&#xff0c;因此可以使用带 alpha 通道的 webM 格式视频满足要求。 要得到透明 webM 格式视频&#xff0c;则需要来源…

Android BottomSheetDialog设置背景透明无效?(解决)

BottomSheetDialog修改背景圆角 解决方法 这里记录一个实际开发过程中遇到的问题&#xff0c;在日常开发中遇到底部弹窗的时候我会第一时间用到BottomSheetDialog&#xff0c;常规的使用就是有一个默认从底部出现的弹窗&#xff0c;但是为了美观&#xff0c;通常会使用圆角&…

dialog设置背景透明

默认dialog是一个白底方形的&#xff0c;如果在xml设置中设置了一种边角是弧形的背景图片&#xff0c;那么显现的dialog角落就会留白 &#xff0c;解决办法&#xff1a; dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent); 效果&#xff1a;只…

Flutter bottomNavigationBar背景透明

Scaffold(extendBody: true,//加这句背景就透明 ..... ) 效果图

html+页面的背景透明,css设置背景透明 元素不透明

css设置背景透明 元素不透明 在做前端页面的时候&#xff0c;我们会遇到这样的情况&#xff0c;需要背景为半透明状态&#xff0c;但是层里面的内容不需要为透明的状态。有时候我们设置的时候会出现不管内容还是背景同时都成透明了&#xff0c;如何实现背景色透明但内容不透明这…

qt实现窗口背景透明

方法一&#xff1a; MainWindow w;//方法一&#xff1a;主界面透明&#xff0c;界面里面的控件不透明w.setWindowFlags(Qt::FramelessWindowHint);//设置无窗口框架边界w.setAttribute(Qt::WA_TranslucentBackground);//设置背景透明w.show(); 编辑界面&#xff1a; 运行效果&…

android fragment 设置透明,DialogFragment背景透明设置

一 、背景 使用自定义DialogFragment实现弹窗效果时,边缘透明图片作为背景图片,依然存在不透明背景; QQ截图20180428154111.png 修复后: QQ截图20180428153910.png 二、实现 因为项目中多个地方使用到DialogFragment,所以稍作了一下封装: import android.app.Dialog; imp…

winform label背景透明

开发时要把label的背景&#xff0c;透明。 在网上找到资料。原文How to Use Transparent Images and Labels in Windows Forms - CodeProject 主要是在用web的 Transparent。&#xff08;我在代码中来设置Color.Transparent没有效果。这个在wpf中设置是可以的&#xff0c;在wi…

9.CSS 背景(background)

CSS 背景(background) CSS 可以添加背景颜色和背景图片&#xff0c;以及来进行图片设置。 background-color背景颜色background-image背景图片地址background-repeat是否平铺background-position背景位置background-attachment背景固定还是滚动背景的合写&#xff08;复合属性&…

将图片背景设置为透明的方法介绍

本文主要介绍使用 Windows 系统自带的“画图 3D”应用程序将图片背景设置为透明的具体方法。 1 需要的应用程序 Windows10 操作系统自带的“画图 3D”应用程序&#xff0c;如下图所示&#xff1a; 2 方法介绍 现有一张背景图为蓝色的 png 格式图片&#xff0c;如下图所示&am…

将图片背景处理为透明的方法步骤

将图片背景处理为透明的方法步骤 1 简单处理图片2 将背景设置为统一颜色3 将背景设置为统一颜色4 保存图片5 一些说明 将一张图片的背景处理成透明&#xff0c;是在工作生活中经常碰到的问题&#xff0c;这个问题的解决可以通过PS这个图像处理神器完成。对于这样的图片处理&…

面对996,程序员如何利用“碎片时间”涨薪?

图片来源|视觉中国 作为一个程序员&#xff0c;需要不断学习更新知识技能来提升自己。但爆炸式的信息量&#xff0c;总使人抓不到学习重点。 所以&#xff0c;笔者为大家筛选了几个程序员会阅读的小众公众号&#xff0c;覆盖全面&#xff0c;囊括了不同的技术类别。小道消息&am…

@程序员,对抗 996,你真的准备好了吗?

4 月 23 日是世界读书日&#xff0c;就在今天 CSDN 举办了码农读书节&#xff01;助力你的技术生涯&#xff01;让你实现歪道超车~全场所有 Python、Java书籍都参与满减活动&#xff0c;最高减 60 元哦&#xff01;今日购书可以领取无门槛优惠券&#xff0c;满减券优惠券可以叠…