
本文介绍如何使用 pandas 对每条记录按指定日期范围逐日展开,并依据时间条件(开仓早于当前日、平仓晚于当前日或未平仓、且平仓类型匹配)生成带 currentdate 的宽表结果。
本文介绍如何使用 pandas 对每条记录按指定日期范围逐日展开,并依据时间条件(开仓早于当前日、平仓晚于当前日或未平仓、且平仓类型匹配)生成带 currentdate 的宽表结果。
在金融、用户行为分析或状态快照类场景中,常需将静态的“生命周期区间”(如开户日、平仓日)展开为每日粒度的状态快照。本教程演示如何高效实现这一转换:对原始 DataFrame 中每一行,在给定日期范围内(如 2024-06-01 至 2024-12-10)遍历每个日期,判断该日期是否落在该记录的有效活跃期内,并将符合条件的组合(含新增 CurrentDate 列)汇总为新 DataFrame。
核心逻辑解析
对应 SQL 条件:
WHERE s.OpenDate < @currentDate AND ISNULL(s.CloseDate, '2050-01-01') > @currentDate AND s.CloseType IN (0, 1, 3, 7, 8)
在 Pandas 中等价实现为:
row['OpenDate'] :开仓日严格早于当前日;-
(pd.isnull(row['CloseDate']) or row['CloseDate'] > current_date):平仓日为空(视为长期有效)或晚于当前日; -
row['CloseType'] in [0, 1, 3, 7, 8]:仅保留指定的业务状态类型。
完整实现代码
import pandas as pd
from datetime import datetime
# 构建示例数据(注意:日期列需转为 datetime 类型)
data = {
'User_Id': [12, 14, 34, 93],
'OpenDate': ['2024-07-14', '2021-12-26', '2022-05-22', '2021-12-06'],
'CloseDate': ['2024-08-18', '2021-12-26', '2022-06-12', '2021-12-06'],
'CloseType': [3, 6, 3, 6]
}
df = pd.DataFrame(data)
df['OpenDate'] = pd.to_datetime(df['OpenDate'])
df['CloseDate'] = pd.to_datetime(df['CloseDate'], errors='coerce') # 自动转 NaT 处理缺失
# 定义日期范围(推荐使用 pd.date_range,更简洁可靠)
date_range = pd.date_range(start='2024-06-01', end='2024-12-10', freq='D')
# 初始化结果列表
results = []
# 双重循环:外层遍历原始行,内层遍历每日
for _, row in df.iterrows():
for current_date in date_range:
# 应用三重条件判断
if (row['OpenDate'] < current_date and
(pd.isna(row['CloseDate']) or row['CloseDate'] > current_date) and
row['CloseType'] in [0, 1, 3, 7, 8]):
results.append({
'CurrentDate': current_date,
'User_Id': row['User_Id'],
'OpenDate': row['OpenDate'],
'CloseDate': row['CloseDate'],
'CloseType': row['CloseType']
})
# 构建结果 DataFrame(自动推断列顺序与类型)
result_df = pd.DataFrame(results)
print(f"共生成 {len(result_df)} 条有效状态快照")
print(result_df.head(10))关键注意事项
- ✅ 日期类型必须统一:务必调用
pd.to_datetime()转换OpenDate和CloseDate,否则比较运算会失败或产生意外结果; - ⚠️ 性能提示:双重循环在大数据集(如万级记录 × 千级日期)下可能较慢;若需优化,可考虑向量化方案(如
pd.merge_asof或布尔索引广播),但逻辑复杂度显著上升; - ? CloseType 过滤需严格匹配:示例中用户 ID 14 和 93 的
CloseType=6不满足[0,1,3,7,8],因此全程无输出——请根据实际业务校验白名单; - ? 日期边界处理:
pd.date_range默认包含首尾,与 SQL 中BETWEEN行为一致;若需排除结束日,可设end=end - pd.Timedelta(days=1); - ? 扩展建议:结果 DataFrame 可进一步按
CurrentDate排序,或添加days_active = (current_date - row['OpenDate']).days等衍生字段增强分析维度。
该方法结构清晰、逻辑透明,适用于中小规模数据的灵活状态展开,是构建时间序列特征或生成报表底表的实用基础技术。

















