改变频率
数据很少恰好是你需要的频率。逐笔数据以毫秒到达,模型要日线,报告要月度。重采样会把时间序列重新划分到新的周期中。由细到粗(日到月)是降采样:每个新周期包含多个旧观测,所以必须说明如何合并;由粗到细(周到日)是升采样:出现了没有数据的新周期,所以必须说明填入什么。
import pandas as pd
daily = pd.Series(
[100.0, 101, 103, 102, 104, 105, 103, 106],
index=pd.bdate_range("2026-02-25", periods=8), # Wed 25 Feb ... Fri 6 Mar
name="close",
)
print(daily.resample("ME").last()) # the last price of each month ("ME" = month end)
# 2026-02-28 103.0
# 2026-03-31 106.0
# Freq: ME, Name: close, dtype: float64
print(daily.resample("W-FRI").last()) # the last price of each week, weeks ending on Friday
# 2026-02-27 103.0
# 2026-03-06 106.0
# Freq: W-FRI, Name: close, dtype: float64选择聚合方式
单独的 series.resample("ME") 什么也不做,它只是描述周期。随后的聚合才决定取值,并且必须符合数据的含义。价格是快照,因此一个月用它的 last(或 first)值表示;成交量是成交数量,所以一个月取 sum;区间用 max 和 min。标签遵循规则:"ME" 用月份的最后一个自然日标记(即使是周六),"W-FRI" 用那一周的周五标记。
volume = pd.Series([10, 20, 30, 40, 50, 60, 70, 80], index=daily.index)
print(volume.resample("ME").sum().to_dict())
# {Timestamp('2026-02-28 00:00:00'): 60, Timestamp('2026-03-31 00:00:00'): 300}
# Several summaries at once
print(daily.resample("ME").agg(["first", "last", "max"]))
# first last max
# 2026-02-28 100.0 103.0 103.0
# 2026-03-31 102.0 106.0 106.0由逐笔数据生成K线与升采样
图表和模型使用 OHLC K线:每个周期的开盘(第一个价格)、最高、最低和收盘(最后一个价格)。resample("D").ohlc() 一次调用就能由日内价格生成它们。没有任何成交的自然日(如周末)仍会以 NaN 行出现,所以只想保留有交易的日子时,最后加上 dropna()。升采样则相反:resample("D").ffill() 在无数据的日子重复最近已知的值,这正是每周报价的价格的真实含义。价格永远不要用 bfill:它把未来的值向前复制,把明天的信息泄露到今天。
times = ["2026-03-05 09:30", "2026-03-05 11:00", "2026-03-05 15:59",
"2026-03-09 09:30", "2026-03-09 12:00", "2026-03-09 15:59"]
ticks = pd.Series([100.0, 101.5, 99.8, 100.7, 102.0, 101.0], index=pd.to_datetime(times))
bars = ticks.resample("D").ohlc()
print(bars)
# open high low close
# 2026-03-05 100.0 101.5 99.8 99.8
# 2026-03-06 NaN NaN NaN NaN
# 2026-03-07 NaN NaN NaN NaN
# 2026-03-08 NaN NaN NaN NaN
# 2026-03-09 100.7 102.0 100.7 101.0
print(bars.dropna().index.strftime("%d %b").tolist()) # ['05 Mar', '09 Mar'] only the days that traded# Upsampling: observations on the 2nd, 6th and 10th, spread over every day with ffill
weekly = pd.Series([100.0, 103.0, 101.0], index=pd.to_datetime(["2026-03-02", "2026-03-06", "2026-03-10"]))
print(weekly.resample("D").ffill().tolist())
# [100.0, 100.0, 100.0, 100.0, 103.0, 103.0, 103.0, 103.0, 101.0]
print(int(weekly.resample("D").mean().isna().sum())) # 6 without a fill rule the new days are empty重采样可能泄露未来信息月度K线要到月底才能知道。如果把月末收盘价挂在该月第一天,策略就像预知了未来。像
"ME" 那样把标签放在周期末尾,升采样用 ffill(绝不用 bfill),并且把月度信号向前平移后再用于日度数据。series.resample(规则)描述周期("D"、"W-FRI"、"ME"、"h");其后的聚合计算取值。- 价格用
last或ohlc(),成交量用sum;空周期显示为NaN,用dropna()去掉。 - 升采样需要填充规则:
ffill()重复最近已知的值;价格绝不用bfill(),它会泄露未来。
下一节:滚动窗口,在最近若干期的滑动窗口上计算移动平均等统计量。
实验:评分内容
- 用
month_end_close取每个月的最后一个价格25 分 - 用
weekly_volume汇总每周的成交量25 分 - 用
daily_ohlc由日内价格生成日K线25 分 - 用
fill_to_daily把不规则的观测铺展到每一天25 分