如何用Python重新加权使两样本协变量分布与样本1一致?
匹配两样本多变量分布的Python实现方案
这是一个非常常见的样本校准需求,尤其在因果推断、样本对齐等场景中高频出现。下面我会给你几种实用的Python实现方案,从直观的事后分层到灵活的逆概率加权都有覆盖:
一、事后分层(Post-Stratification):适合低维分类变量
事后分层是最直接的方法,尤其当你的两个变量都是分类变量时。核心思路是把两个变量交叉划分为若干层,让样本2每层的加权占比和样本1完全一致。
代码实现(用Pandas手动实现)
先模拟两组样本(你可以直接替换成自己的真实数据):
import pandas as pd import numpy as np # 固定随机种子保证结果可复现 np.random.seed(42) # 样本1:变量A(二元)和变量B(三元),预设目标分布 sample1 = pd.DataFrame({ 'A': np.random.binomial(1, 0.6, size=1000), 'B': np.random.choice(['低', '中', '高'], size=1000, p=[0.3, 0.5, 0.2]) }) # 样本2:和样本1分布差异较大的数据集 sample2 = pd.DataFrame({ 'A': np.random.binomial(1, 0.3, size=800), 'B': np.random.choice(['低', '中', '高'], size=800, p=[0.6, 0.3, 0.1]) })
接下来计算分层权重:
# 计算样本1中每个交叉层的占比(目标分布) stratum_target = sample1.groupby(['A', 'B']).size() / len(sample1) # 计算样本2中每个交叉层的占比(当前分布) stratum_current = sample2.groupby(['A', 'B']).size() / len(sample2) # 计算权重:目标占比 / 当前占比,处理样本2中缺失的层(设权重为0,后续可直接丢弃) stratum_weights = stratum_target / stratum_current stratum_weights = stratum_weights.fillna(0) # 给样本2的每条观测分配对应层的权重 sample2['post_strat_weight'] = sample2.apply( lambda row: stratum_weights.loc[(row['A'], row['B'])], axis=1 )
验证效果
你可以直接对比加权前后样本2的分布和样本1是否一致:
# 样本1的原始目标分布 print("样本1的目标分布:\n", stratum_target.round(3)) # 加权后样本2的分布 weighted_dist = sample2.groupby(['A', 'B'])['post_strat_weight'].sum() / sample2['post_strat_weight'].sum() print("\n加权后样本2的分布:\n", weighted_dist.round(3))
如果你的变量是连续型,可以先通过pd.cut()分箱把连续变量转换成分类变量,再套用上面的方法。
二、逆概率加权(IPW):适合连续/高维变量
如果你的变量包含连续型,或者变量维度较高,事后分层的分层数会指数级增长,这时候逆概率加权(Inverse Probability Weighting)更合适。它通过模型(比如逻辑回归、树模型)估计样本2中每条观测"属于样本1"的概率,权重就是这个概率的调整比值。
代码实现(用Statsmodels做逻辑回归)
import statsmodels.api as sm from statsmodels.formula.api import logit # 合并两组样本,标记是否为样本1(1=样本1,0=样本2) combined_data = pd.concat([ sample1.assign(is_sample1=1), sample2.assign(is_sample1=0) ]) # 拟合逻辑回归模型,预测样本2属于样本1的概率 model = logit('is_sample1 ~ A + B', data=combined_data).fit(disp=False) # 计算IPW权重:样本1的概率 / 样本2的概率 sample2['ipw_weight'] = model.predict(sample2) / (1 - model.predict(sample2))
这种方法不需要手动分层,模型会自动捕捉变量间的关联。如果变量关系复杂,你也可以用XGBoost、LightGBM等树模型替代逻辑回归,提升预测精度。
三、用专门的库简化操作
如果你不想手动实现细节,可以用现成的统计库来处理:
- survey库:专门用于复杂抽样和加权分析,支持事后分层、IPW等多种加权方式:
from survey import Survey # 基于事后分层权重创建调查对象 svy = Survey(sample2, weights='post_strat_weight') # 直接计算加权后的统计量,比如变量A的均值、变量B的分布 print("加权后A的均值:", svy.mean('A').round(3)) print("加权后B的分布:\n", svy.distribution('B'))
- causalml库:如果你的需求和因果推断相关,这个库提供了更丰富的加权工具,包括倾向得分加权等。
注意事项
- 如果样本2中某层在样本1中完全不存在,对应的权重会是0,这部分观测建议直接丢弃,因为无法通过加权匹配目标分布。
- 如果样本2中某层的占比极小,权重会非常大,可能导致后续分析的方差变大,这时候可以考虑对权重做截断处理(比如限制权重在某个范围内),或者用正则化模型来平滑。
- 加权后一定要验证分布是否匹配,避免出现计算错误。
内容的提问来源于stack exchange,提问作者spieler34
相关产品推荐
相关产品推荐

