
本文介绍一种基于自定义日期逻辑和分组迭代的方法,用于在含多个产品id的时间序列数据中,精准识别价格在连续工作日(跳过周末)保持不变的区间,并统计其天数。
本文介绍一种基于自定义日期逻辑和分组迭代的方法,用于在含多个产品id的时间序列数据中,精准识别价格在连续工作日(跳过周末)保持不变的区间,并统计其天数。
在金融、零售或供应链分析中,常需识别商品价格“冻结期”——即价格在连续工作日(而非日历日)内未发生变动的时段。由于真实交易场景中周末无报价,2024-02-01(周四)与2024-02-04(周一)应视为连续,而简单按日期差值或shift().cumsum()分组会因忽略业务日历导致错误合并。
核心挑战在于三点:
✅ 跨ID隔离:不同产品的价格序列必须完全独立处理;
✅ 工作日连续性校验:需动态计算“下一个有效交易日”,自动跳过周六(dayofweek=5)和周日(dayofweek=6),周五(dayofweek=4)后应跳至下周一;
✅ 严格相邻性判断:仅当当前记录的日期等于前一条记录的“预期下一交易日”、且ID与价格均相同时,才纳入同一稳定区间。
以下为完整可执行解决方案(基于 pandas 和原生 Python 迭代,兼顾清晰性与鲁棒性):
import pandas as pd
def get_next_trading_day(d):
"""给定一个日期,返回下一个工作日(跳过周末)"""
d = d + pd.Timedelta("1 day")
if d.dayofweek == 4: # Friday → skip to Monday
d += pd.Timedelta("2 days")
elif d.dayofweek == 5: # Saturday → skip to Monday
d += pd.Timedelta("1 day")
return d
def generate_stable_price_intervals(df):
"""
输入:DataFrame,列名必须为 'ID', 'Date', 'Price'
输出:DataFrame,含 ID, start_date, end_date, price, repeated_days_count
"""
df = df.sort_values(['ID', 'Date']).reset_index(drop=True)
df['Date'] = pd.to_datetime(df['Date']) # 确保日期类型
results = []
current_group = []
prev_id = None
prev_price = None
expected_next_day = None
for _, row in df.iterrows():
i, d, p = row['ID'], row['Date'], row['Price']
# 判断是否延续当前稳定区间
is_continuation = (
prev_id == i
and prev_price == p
and expected_next_day == d
)
if not is_continuation:
# 结束上一组(若存在)
if len(current_group) >= 1:
results.append({
'ID': current_group[0][0],
'start_date': current_group[0][1],
'end_date': current_group[-1][1],
'price': current_group[0][2],
'repeated_days_count': len(current_group)
})
# 新建组
current_group = [(i, d, p)]
else:
current_group.append((i, d, p))
# 更新状态
prev_id, prev_price = i, p
expected_next_day = get_next_trading_day(d)
# 处理最后一组
if current_group:
results.append({
'ID': current_group[0][0],
'start_date': current_group[0][1],
'end_date': current_group[-1][1],
'price': current_group[0][2],
'repeated_days_count': len(current_group)
})
return pd.DataFrame(results)
# 示例使用
df = pd.DataFrame({
'ID': [1,1,1,1,1,2,2,2,2,2],
'Date': ['2024-02-01','2024-02-04','2024-02-05','2024-02-06','2024-02-07',
'2024-02-01','2024-02-04','2024-02-05','2024-02-06','2024-02-07'],
'Price': ['1,00','1,00','1,20','1,30','1,30',
'1,30','0,90','0,90','0,90','1,30']
})
out = generate_stable_price_intervals(df)
print(out)关键注意事项:
? 数据必须预先按 ID 和 Date 升序排序,否则逻辑失效;
? Price 字段若为数值型(如 float),请确保无精度误差(建议用 round(price, 2) 或 decimal 类型比对);
? get_next_trading_day 函数假设标准周一至周五交易日历;如需支持节假日,应集成 pandas.offsets.CustomBusinessDay;
? 该方法时间复杂度为 O(n),优于多次 groupby + diff() 的嵌套操作,且逻辑透明、易于调试和扩展。
最终输出严格匹配需求:每个稳定价格区间对应一行,repeated_days_count 表示该区间内实际交易日数量(非日历天数),且跨ID、跨价格段绝不混淆。

















