如何对Pandas面板数据进行连续3个月周期的Bootstrap抽样?
Pandas面板数据的连续3个月Bootstrap抽样实现
第一步:先把日期格式理清楚
不管你的月份是Nov21这种字符串还是其他格式,先转成Pandas的datetime类型,这是处理跨年度连续月份(比如Nov21-Dec21-Jan22)的核心前提:
import pandas as pd import numpy as np # 假设数据包含customer_id(客户ID)和month(月份列)两个核心字段 df['month'] = pd.to_datetime(df['month'], format='%b%y')
如果你的月份格式是2021-11这类,把format参数改成'%Y-%m'即可。
第二步:给每个客户筛选有效连续3个月的起始点
先按客户+月份排序,再给每个客户的月份编连续序号,这样不管客户数据有没有缺失月份,都能准确找到可抽取的连续3个月区间:
# 按客户和月份排序,确保时间顺序正确 df_sorted = df.sort_values(['customer_id', 'month']).reset_index(drop=True) # 给每个客户的月份从1开始编序号 df_sorted['month_rank'] = df_sorted.groupby('customer_id')['month'].rank(method='first').astype(int) # 筛选有效起始序号:序号必须满足后面还有至少2个月份(凑够连续3个月) valid_starts = df_sorted.groupby('customer_id').apply( lambda x: x['month_rank'].unique()[x['month_rank'].unique() <= x['month_rank'].max() - 2] ).reset_index(name='start_rank')
如果你的数据中每个客户的月份都是连续无缺失的,这一步可以简化,直接取每个客户前n-2个月份作为起始点:
# 简化版(仅适用于客户月份完全连续的场景) valid_starts = df_sorted.groupby('customer_id')['month'].apply( lambda x: x.iloc[:-2] ).reset_index(name='start_month')
第三步:编写抽样函数,执行90次抽样
每次随机挑选一个有有效区间的客户,再随机选他的一个起始点,抽取对应连续3个月的数据:
def bootstrap_sample(df_sorted, valid_starts): # 随机选一个符合条件的客户 sampled_customer = np.random.choice(valid_starts['customer_id'].unique()) # 选该客户的一个有效起始序号 customer_starts = valid_starts[valid_starts['customer_id'] == sampled_customer]['start_rank'].values sampled_start = np.random.choice(customer_starts) # 抽取连续3个月的数据 sample = df_sorted[ (df_sorted['customer_id'] == sampled_customer) & (df_sorted['month_rank'].between(sampled_start, sampled_start + 2)) ] return sample # 重复90次抽样,合并结果 bootstrap_results = pd.concat([bootstrap_sample(df_sorted, valid_starts) for _ in range(90)], ignore_index=True)
如果用的是简化版的valid_starts,抽样函数可以改成这样:
def bootstrap_sample_simple(df_sorted, valid_starts): sampled_customer = np.random.choice(valid_starts['customer_id'].unique()) sampled_start = np.random.choice(valid_starts[valid_starts['customer_id'] == sampled_customer]['start_month'].values) # 计算结束月份:起始月+2个月 sampled_end = sampled_start + pd.DateOffset(months=2) sample = df_sorted[ (df_sorted['customer_id'] == sampled_customer) & (df_sorted['month'] >= sampled_start) & (df_sorted['month'] <= sampled_end) ] return sample
额外实用提示
- 提前过滤数据不足3个月的客户,避免抽样时选到无效客户:
df = df.groupby('customer_id').filter(lambda x: len(x) >= 3)
- 如果需要给每次抽样打标记方便后续分析,可以在抽样函数中添加
sample_id列:
def bootstrap_sample_with_id(df_sorted, valid_starts, sample_id): sample = bootstrap_sample(df_sorted, valid_starts) sample['sample_id'] = sample_id return sample # 循环时传入抽样编号 bootstrap_results = pd.concat( [bootstrap_sample_with_id(df_sorted, valid_starts, i+1) for i in range(90)], ignore_index=True )
内容的提问来源于stack exchange,提问作者Katharina Böhm
相关产品推荐
相关产品推荐

