因果推断--PSM的原理和应用(二)

2024-01-13 08:10
文章标签 应用 原理 推断 因果 psm

本文主要是介绍因果推断--PSM的原理和应用(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

一、因果推断简要介绍

二、PSM原理

2.1 前提条件

2.2 实施步骤

三、应用案例代码

3.1 自己实现psm

1. 数据读入及各特征显著性检验

2. 建模及添加psm得分

​3. 查看实验组和对照组倾向得分的重叠度

4. 用knn为实验组元素匹配样本

5. 对比匹配前后各个特征在实验组、对照组上的Cohen's d效应值、p值

3.2 用psmpy包来实现

1. psm模型初始化并计算psm得分

2. 为实验组匹配元素,并画出匹配后的得分分布

3. 画出匹配前后的效应值差异

4. 其他属性及方法

四、总结

4.1 psm的优缺点


一、因果推断简要介绍

        上一篇介绍了DID,本文介绍和DID可一起搭配使用的一种因果推断方法PSM。在正式介绍PSM之前,先来简单回顾一下为什么需要因果推断以及如何做因果推断。我们都知道相关非因果,比起相关我们更希望知道两个事件之间的因果关系,有了确切的因果关系,才能知道对A如何操作才能引起B的变化。解决因果推断问题,目前主要有两种框架,Rubin虚拟事实模型和Pearl因果图模型。站在数据分析的角度上,Rubin虚拟事实模型更容易理解,其核心思想是寻找恰当的对照组。想要知道策略干预带来的效果是多少,我们需要知道该用户在干预和未干预下的表现,但同一时间内无法观测到一个用户的两种状态,所以我们就需要借助AB实验或者构造合适对照组的方式来进行评估。除了AB实验外常用的方法有:双重差分、倾向得分匹配、合成控制、断点回归、双重机器学习、因果随机森林、Uplift Model等。

二、PSM原理

        PSM的核心思想是从未受干预的用户群体中,找到和干预对象一模一样的用户,这样就可以把因果效应归因到干预上。一般来说,每个样本有多个特征,对于一个用户来说有:画像特征、行为特征等,如果每个特征都进行匹配,则维度会很多,匹配也会很困难。所以可引进倾向得分P(x)来代替,这样就可以用一维变量进行匹配,从而大大降低匹配的复杂度。

        这里个体的倾向得分是指在个体情况一定的前提下,进入处理组的概率。倾向得分可用分类模型得到,选择尽可能多的协变量,类似于回归中的控制变量。计算倾向得分类似于降维的过程,把非常多的协变量维度降为一个维度,就是倾向得分。两个个体的倾向得分非常接近,并不意味着这两个个体的其他属性也接近,这不好判断。不过根据PSM的原理,倾向得分接近就够了,其他属性也接近更好。匹配后可进一步验证其他属性是否更接近了。

2.1 前提条件

(1)样本量要足够大,如果太小,很多干预组的样本可能在控制组匹配不到对象,或者能匹配但是距离很远,也就是匹配度不高。

(2)干预组与控制组的倾向得分要有较大的共同取值范围,否则会丢失较多样本,导致匹配的样本不具代表性。

2.2 实施步骤

1. 倾向得分计算:因变量为是否被干预,自变量为用户特征,建立LR/XGB/RF等模型预测用户进入干预组的概率,以该概率作为倾向得分,也可以其Logit( ln(e(x)/1-e(x) )作为倾向得分。

2. 倾向得分匹配:为干预组用户,在非干预组中匹配一个近似相同的用户,作为对照组

        a. 修剪:去除掉倾向得分比较极端的用户,常见的做法是保留得分在[a, b]这个区间的用户。实验组和对照组得分区间的交集,只保留区间中部的90%或95%,如原始得分在[0.05, 0.95]的用户。

        b. 匹配:实验组和对照组用户根据得分进行匹配。常用方法有knn和radius,knn即对实验组进行1对k有放回或无放回匹配。radius:对实验组用户,匹配所有得分差异小于radius的用户。    

        c. 设置得分差异上限:当我们匹配用户时,要求每一对用户的得分差异不超过指定的上限。

3. 平衡性检验:用Cohen’s d效应量衡量匹配后的实验组和对照组在各个特征上的差异,其计算公式如下:

d = \frac{\bar{x_{t}} - \bar{x_{c}}}{s}

s = \frac{\sqrt{(n_{t} - 1)s_{t}^{2} + (n_{c} - 1)s_{c}^{2} }}{n_{t} + n_{c} - 2 }

该值的分子为实验组和对照组平均值之差,分母为两组的汇总方差,一般该值不超过0.2则认为匹配通过平衡性检验。

4. 因果效应估计:我们的目标是计算策略干预组的ATT(Average Teamtment Effect on then Treated)。可用匹配到的干预组和对照组计算核心指标的提升情况,从而得到因果效应。

三、应用案例代码

3.1 自己实现psm

1. 数据读入及各特征显著性检验
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import math# 1. 引入数据
df = pd.read_csv('./data/groupon.csv')df.head()
# 2. 查看实验组/对照组各特征的均值
df.groupby('treatment').count()
df.groupby('treatment').mean()# 3. 实验组/对照组各特征差异显著性 
df_control = df[df.treatment==0]
df_treatment = df[df.treatment==1]# 以 revenue 为例 
from scipy.stats import ttest_ind# 查看两组的均值
print(df_control.revenue.mean(), df_treatment.revenue.mean()) # t检验
_, p = ttest_ind(df_control.revenue, df_treatment.revenue)
print(f'p={p:.3f}')# 输出是否差异是否显著
alpha = 0.05  # 显著水平设为 0.05
if p > alpha:print('两者分布无差异(没有足够的证据拒绝原假设)')
else:print('两者分布差异显著(拒绝原假设)')

2. 建模及添加psm得分

a. 建立逻辑回归模型

# 1. 模型数据准备
X = df[['prom_length', 'price', 'discount_pct', 'coupon_duration', 'featured', 'limited_supply']]
y = df['treatment']# 2. 建立逻辑回归模型,预测用户进入干预组的概率
from sklearn.linear_model import LogisticRegression
lr = LogisticRegression()
lr.fit(X, y)# 查看逻辑回归模型各个特征的系数
coeffs = pd.DataFrame({'column':X.columns.to_numpy(),'coeff':lr.coef_.ravel(),
})
coeffs

b. 添加psm得分

# 3. 添加倾向匹配得分
df['ps'] = pred_prob[:, 1]# 添加logit后的得分
def logit(p):logit_value = math.log(p / (1-p))return logit_valuedf['ps_logit'] = df.ps.apply(lambda x: logit(x))df.head()
3. 查看实验组和对照组倾向得分的重叠度
sns.histplot(data=df, x='ps', hue='treatment')

可以看到实验组和对照组的倾向得分在0.05-0.6之间有较多的重合,所以可进行匹配。

4. 用knn为实验组元素匹配样本

a. 首先为每个元素都进行knn,匹配得分范围在25%标准差内的10个最近的元素

from sklearn.neighbors import NearestNeighbors# 1. 以ps得分标准差的25%作为半径,匹配相邻的10个元素,半径越大或者k越大,可匹配到的元素越多
n_neighbors = 10
caliper = np.std(df.ps) * 0.25
print(f'caliper (radius) is: {caliper:.4f}')# 2. knn
knn = NearestNeighbors(n_neighbors=n_neighbors, radius=caliper)ps = df[['ps']]  
knn.fit(ps)# 3. 返回每个点相邻点的索引和距离
distances, neighbor_indexes = knn.kneighbors(ps)  print(neighbor_indexes.shape)  # 输出neighbor_indexes的长度# 查看第一个元素的匹配结果
print(distances[0])
print(neighbor_indexes[0])

 b. 为实验组中的每个元素,在对照组中找到一个和它匹配的

matched_control = []  # 保存对照组中匹配到的观测对象for current_index, row in df.iterrows():  # 遍历 dataframe中的每行记录if row.treatment == 0:   # 如果当前行是对照组df.loc[current_index, 'matched'] = np.nan  # 将匹配到的对象设置为nanelse: for idx in neighbor_indexes[current_index, :]: # 遍历实验组样本的10个相邻元素# 确保当前行不在10个相邻元素中,既排除自己# 并且确保相邻元素在对照组中if (current_index != idx) and (df.loc[idx].treatment == 0):if idx not in matched_control:  # 当前对象没有被匹配过df.loc[current_index, 'matched'] = idx  # 记录当前的匹配对象matched_control.append(idx)  # 并将其插入到待保存的数组中breakdf.head()

5. 对比匹配前后各个特征在实验组、对照组上的Cohen's d效应值、p值
from numpy import mean
from numpy import var
from math import sqrt# 计算Cohen's d 的方法
def cohen_d(d1, d2):# 计算样本数量n1, n2 = len(d1), len(d2)# 计算样本方差s1, s2 = var(d1, ddof=1), var(d2, ddof=1)# 计算两者的组合方差s = sqrt(((n1 - 1) * s1 + (n2 - 1) * s2) / (n1 + n2 - 2))# 计算均值u1, u2 = mean(d1), mean(d2)# 得到效应值return (u1 - u2) / seffect_sizes = []
cols = ['prom_length', 'price', 'discount_pct', 'coupon_duration', 'featured','limited_supply']for cl in cols:_, p_before = ttest_ind(df_control[cl], df_treatment[cl])_, p_after = ttest_ind(df_matched_control[cl], df_matched_treatment[cl])cohen_d_before = cohen_d(df_treatment[cl], df_control[cl])cohen_d_after = cohen_d(df_matched_treatment[cl], df_matched_control[cl])effect_sizes.append([cl,'before', cohen_d_before, p_before])effect_sizes.append([cl,'after', cohen_d_after, p_after])

可以看到匹配后除了 discount_pct 这个特征外,其余特征的 Cohen's d 效应值都有所下降,且p值都大于0.05,说明匹配后的这些特征的分部较为均衡,无显著差异。

用柱状图更直观的输出各特征Cohen's d 效应值的前后差异

# discount_pct and featured are not significant, all other features are more balanced after matching
fig, ax = plt.subplots(figsize=(15, 5))
sns.barplot(data=df_effect_sizes, x='effect_size', y='feature', hue='matching', orient='h')

3.2 用psmpy包来实现

1. psm模型初始化并计算psm得分
from psmpy import PsmPy
from psmpy.plotting import *df_psmpy = pd.read_csv('./data/groupon.csv')
df_psmpy.info()# 初始化
# exclude:  模型拟合时要忽略的字段
# indx: 唯一值
psm = PsmPy(df_psmpy, treatment='treatment', indx='deal_id', exclude = ['min_req', 'start_date', 'fb_likes', 'quantity_sold', 'revenue'])# 计算logit得分
psm.logistic_ps(balance=True)  # balance=False 建模时是否让实验组和对照组的记录数保持平衡
psm.predicted_data  # 查看进入模型中的数据

2. 为实验组匹配元素,并画出匹配后的得分分布

# knn 1:1 匹配
psm.knn_matched(matcher='propensity_logit', replacement=False, caliper=None)# 画出倾向得分的分布图
# x轴是倾向得分,y轴是匹配到的数量 
psm.plot_match(Title='Matching Result', Ylabel='# of obs', Xlabel= 'propensity logit', names = ['treatment', 'control'])

 

3. 画出匹配前后的效应值差异
# 画出各个特征在匹配前后的效应值
psm.effect_size_plot()

 

4. 其他属性及方法
# 输出匹配到的id
psm.matched_ids# 输出匹配到的原始数据
psm.df_matched# 输出匹配前后的效应值
psm.effect_size# 单独使用
from psmpy.functions import cohenD
cohenD(df_psmpy, treatment = 'treatment', metricName = 'prom_length')

 

四、总结

4.1 psm的优缺点

优点:psm能够得到和实验组较为相似的对照组,配合DID进行平衡性检验后,能够得到较为可信的因果效应,整体实现起来也较为简单。

缺点:

  • 只能缓解由观测变量带来的内生性问题,无法处理由非观测变量产生的内生性问题。
  • 对照组需要有足够大的样本量,要不然匹配时会丢失较多样本,导致匹配结果不具有代表性。这个是硬伤。。

这篇关于因果推断--PSM的原理和应用(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/600770

相关文章

在Ubuntu上部署SpringBoot应用的操作步骤

《在Ubuntu上部署SpringBoot应用的操作步骤》随着云计算和容器化技术的普及,Linux服务器已成为部署Web应用程序的主流平台之一,Java作为一种跨平台的编程语言,具有广泛的应用场景,本... 目录一、部署准备二、安装 Java 环境1. 安装 JDK2. 验证 Java 安装三、安装 mys

Python中构建终端应用界面利器Blessed模块的使用

《Python中构建终端应用界面利器Blessed模块的使用》Blessed库作为一个轻量级且功能强大的解决方案,开始在开发者中赢得口碑,今天,我们就一起来探索一下它是如何让终端UI开发变得轻松而高... 目录一、安装与配置:简单、快速、无障碍二、基本功能:从彩色文本到动态交互1. 显示基本内容2. 创建链

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

java中VO PO DTO POJO BO DO对象的应用场景及使用方式

《java中VOPODTOPOJOBODO对象的应用场景及使用方式》文章介绍了Java开发中常用的几种对象类型及其应用场景,包括VO、PO、DTO、POJO、BO和DO等,并通过示例说明了它... 目录Java中VO PO DTO POJO BO DO对象的应用VO (View Object) - 视图对象

Go信号处理如何优雅地关闭你的应用

《Go信号处理如何优雅地关闭你的应用》Go中的优雅关闭机制使得在应用程序接收到终止信号时,能够进行平滑的资源清理,通过使用context来管理goroutine的生命周期,结合signal... 目录1. 什么是信号处理?2. 如何优雅地关闭 Go 应用?3. 代码实现3.1 基本的信号捕获和优雅关闭3.2

正则表达式高级应用与性能优化记录

《正则表达式高级应用与性能优化记录》本文介绍了正则表达式的高级应用和性能优化技巧,包括文本拆分、合并、XML/HTML解析、数据分析、以及性能优化方法,通过这些技巧,可以更高效地利用正则表达式进行复杂... 目录第6章:正则表达式的高级应用6.1 模式匹配与文本处理6.1.1 文本拆分6.1.2 文本合并6

python中的与时间相关的模块应用场景分析

《python中的与时间相关的模块应用场景分析》本文介绍了Python中与时间相关的几个重要模块:`time`、`datetime`、`calendar`、`timeit`、`pytz`和`dateu... 目录1. time 模块2. datetime 模块3. calendar 模块4. timeit

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,