因果推斷1:PSM傾向得分匹配法

內(nèi)容來自【顧先生聊數(shù)據(jù)】的 PSM傾向得分匹配法【上篇:理論篇】PSM傾向得分匹配法【下篇:python實操篇】

定義:PSM傾向得分匹配法瞧挤,通過對數(shù)據(jù)建模业舍,為每個用戶擬合?個概率(多維特征擬合成一維的概率)冕房,在對照組樣本中尋找和實驗組最接近的樣本,從而進行比較。

前提

  1. 條件獨立假設(shè)践瓷。在接受實驗之前换衬,對照組和實驗組之間沒有差異痰驱,實驗組產(chǎn)生的效應(yīng)完全來自實驗處理。
  2. 共同?撐假設(shè)瞳浦。在理想情況下担映,實驗組的個體,都能在對照組中找到對應(yīng)的個體叫潦。如下圖蝇完,兩組共同取值較少時,就不適用PSM了

代碼
1矗蕊、導(dǎo)入相關(guān)的python庫短蜕,path及model設(shè)置

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 = "E:/pythonFile/data/psm/psm_gxslsj_data.csv"  
#model由干預(yù)項和其他類別標(biāo)簽組成,形式為"干預(yù)項~類別特征+列別特征傻咖。朋魔。。"
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)

2孙援、獲得傾向性匹配得分

df = pd.read_csv(path)
df = df.set_index("use_id")    #將use_id作為數(shù)據(jù)的新索引,這里可以替換成自己想要的索引字段
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
df

3拓售、區(qū)分干預(yù)與非干預(yù)
groups是干預(yù)項,propensity是傾向性匹配得分镶奉,這里要分開干預(yù)與非干預(yù)础淤,且確保n1<n2

groups = df.PUSH                        #將PUSH替換成自己的干預(yù)項
propensity = df.PROPENSITY
#把干預(yù)項替換成True和False
groups = groups == groups.unique()[1]
n = len(groups)
#計算True和False的數(shù)量
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))    #隨機排序?qū)嶒灲M值骇,減少原始排序的影響

4、根據(jù)傾向評分差異將干預(yù)組與對照組進行匹配
注意:caliper = None可以替換成自己想要的精度

matches = {}
k = int(k)
print("\n給每個干預(yù)組匹配 [" + str(k) + "] 個對照組 ... ", end = " ")
for m in m_order:
    # 計算所有傾向得分差異,這里用了最粗暴的絕對值
    # 將propensity[groups==1]分別拿出來移国,每一個都與所有的propensity[groups==0]相減
    dist = abs(g1[m]-g2)
    array = np.array(dist)
    #如果無放回地匹配吱瘩,最后會出現(xiàn)要選取3個匹配對象,但是只有一個候選對照組的錯誤迹缀,故進行判斷
    if k < len(array):
        # 在array里面選擇K個最小的數(shù)字使碾,并轉(zhuǎn)換成列表
        k_smallest = np.partition(array, k)[:k].tolist()
        # 用卡尺做判斷
        caliper = None
        if 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:
            # 如果不用標(biāo)尺判斷,那就直接上k_smallest了
            keep_ids = np.array(dist[dist.isin(k_smallest)].index)
        #  如果keep_ids比要匹配的數(shù)量多祝懂,那隨機選擇下票摇,如要少,通過補NA配平數(shù)量
        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 = False
        if not replace:
            g2 = g2.drop(matches[m])
print("\n匹配完成!")

5砚蓬、將匹配完成的結(jié)果合并起來

matches = pd.DataFrame.from_dict(matches, orient="index")
matches = matches.reset_index()
column_names = {}
column_names["index"] = "干預(yù)組"
for i in range(k):
    column_names[i] = str("匹配對照組_" + str(i+1))
matches = matches.rename(columns = column_names)
matches
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末矢门,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子灰蛙,更是在濱河造成了極大的恐慌祟剔,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摩梧,死亡現(xiàn)場離奇詭異物延,居然都是意外死亡,警方通過查閱死者的電腦和手機仅父,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進店門叛薯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人笙纤,你說我怎么就攤上這事耗溜。” “怎么了省容?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵抖拴,是天一觀的道長。 經(jīng)常有香客問我蓉冈,道長城舞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任寞酿,我火速辦了婚禮家夺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伐弹。我一直安慰自己拉馋,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布惨好。 她就那樣靜靜地躺著煌茴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪日川。 梳的紋絲不亂的頭發(fā)上蔓腐,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天,我揣著相機與錄音龄句,去河邊找鬼回论。 笑死,一個胖子當(dāng)著我的面吹牛分歇,可吹牛的內(nèi)容都是我干的傀蓉。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼职抡,長吁一口氣:“原來是場噩夢啊……” “哼葬燎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起缚甩,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤谱净,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后蹄胰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體岳遥,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年裕寨,在試婚紗的時候發(fā)現(xiàn)自己被綠了浩蓉。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡宾袜,死狀恐怖捻艳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情庆猫,我是刑警寧澤认轨,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站月培,受9級特大地震影響嘁字,放射性物質(zhì)發(fā)生泄漏恩急。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一纪蜒、第九天 我趴在偏房一處隱蔽的房頂上張望衷恭。 院中可真熱鬧,春花似錦纯续、人聲如沸随珠。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窗看。三九已至,卻和暖如春倦炒,著一層夾襖步出監(jiān)牢的瞬間显沈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工析校, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留构罗,地道東北人。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓智玻,卻偏偏與公主長得像遂唧,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子吊奢,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

推薦閱讀更多精彩內(nèi)容