You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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库:如果你的需求和因果推断相关,这个库提供了更丰富的加权工具,包括倾向得分加权等。

注意事项

  1. 如果样本2中某层在样本1中完全不存在,对应的权重会是0,这部分观测建议直接丢弃,因为无法通过加权匹配目标分布。
  2. 如果样本2中某层的占比极小,权重会非常大,可能导致后续分析的方差变大,这时候可以考虑对权重做截断处理(比如限制权重在某个范围内),或者用正则化模型来平滑。
  3. 加权后一定要验证分布是否匹配,避免出现计算错误。

内容的提问来源于stack exchange,提问作者spieler34

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:23:17