resample 换频率:把日数据换成周、月、季,'W' 的标签是每周末。 rolling 开窗口算滑动平均,第一个非空位置是「窗口长度减 1」。 ohlc 一次给四个数,就是 K 线要的开高低收。resample 加 agg 是万能组合。
第 4 章最后一篇,也是整章的综合。前面所有零件在这里装成一台机器。 材料是 60 天的日流水,随机种子固定,所以每次跑出来一样。
一、造一份日流水
pd.date_range('2026-01-01', periods=60, freq='D') 造 60 天日期。
配随机销量和单价,相乘得金额。60 天金额合计 341861。
随机数必须固定种子。不固定的话别人复现不了,报出来的数就是孤证。
种子写 20261102,跑几次都一样。这是可复现分析的最低要求。
二、resample:把日换成周
sale['金额'].resample('W').sum() 按周汇总,得到 9 个点。
第一周(到 2026-01-04)合计 28099,最后一周合计 39992。
'W' 的标签是每周末(周日),不是周一。这一点常把人绕晕。
所以第一周只有 4 天(1 月 1 日到 4 日),不是完整的一周。
要按周一起算,用 freq='W-MON' 之类的写法,标签落在周一。
三、rolling:开个窗口算均值
sale['金额'].rolling(7).mean() 给 7 天滑动平均。
前 6 个是 NaN(窗口没满),第 7 个开始有值:6372.86。
最后一天是 5713.14,说明末尾这几天比 7 天均值低。
第一个非空位置是「窗口长度减 1」。想让它一开始就有值,加 min_periods=1。
窗口越大越平滑,也越迟钝。7 天能看出周节奏,30 天只能看出趋势。
四、ohlc:一次给四个数
| 日期 | open | high | low | close |
|---|---|---|---|---|
| 2026-01-04 | 7560 | 7729 | 5565 | 7729 |
| 2026-01-11 | 7138 | 10382 | 2075 | 4752 |
| 2026-01-18 | 2754 | 8592 | 2754 | 3450 |
open 是窗口第一天的值,close 是最后一天,high 和 low 是窗口内最高最低。 这四个数就是 K 线要的四个数,一行代码凑齐。 ohlc 要求索引已经是时间类型。字符串索引上去直接报错。
五、两个综合案例
案例一:按周看趋势,找出最高的那一周。
w.idxmax() 给 2026-02-22 那一周,合计 44637——比平均周高不少。
案例二:按月比总额和日均,用 resample('ME').agg(['sum', 'mean'])。
| 月份 | sum | mean |
|---|---|---|
| 2026-01 | 167835 | 5414.0 |
| 2026-02 | 171358 | 6119.9 |
| 2026-03 | 2668 | 2668.0 |
三月只有一天,总额 2668 不能和一月二月比。这是时间序列最常见的误读。 比较之前先数一下每期有几天。不完整的期间要么砍掉,要么按日均比。 这一篇的四个动作——resample、rolling、ohlc、agg——就是时间序列的全部基础。
六、动手 Demo:时间序列与两个综合案例
造 60 天流水,做周五汇总、7 日滑动平均、周 ohlc,最后按月比总额和日均。 注意三月只有一天,这个陷阱会现场演示。
import numpy as np
import pandas as pd
RNG = pd.date_range("2026-01-01", periods=60, freq="D")
RND = np.random.default_rng(20261102)
S = pd.DataFrame({"销量": RND.integers(80, 200, 60),
"单价": RND.integers(20, 60, 60)}, index=RNG)
S["金额"] = S["销量"] * S["单价"]
print("合计 %d" % S["金额"].sum())
W = S["金额"].resample("W").sum()
print("周 %d 个点,最高 %s 合计 %d" % (len(W), W.idxmax().date(), W.max()))
R = S["金额"].rolling(7).mean()
print("第 7 天 %.2f,最后一天 %.2f" % (R.iloc[6], R.iloc[-1]))
O = S["金额"].resample("W").ohlc()
print(O.head(2).round(1).to_string())
MO = S["金额"].resample("ME").agg(["sum", "mean"])
print(MO.round(1).to_string())
print("三月只有 %d 天" % (S.index.month == 3).sum())
实跑输出:
--------------------------------------------------------------------
时间序列与两个综合案例
--------------------------------------------------------------------
① 造一份日流水
--------------------------------------------------------------------
60 天的流水,2026-01-01 到 2026-03-01,金额合计 341861。
! 随机数用了固定种子,所以每次跑出来一样——报数的时候必须交代种子,否则别人复现不了。
② resample:把日换成周
--------------------------------------------------------------------
按周汇总,得到 9 个点。
第一周(到 2026-01-04)合计 28099,最后一周合计 39992。
resample('W') 的标签是每周末(周日),不是周一。这一点常把人绕晕。
! 降频用 resample,升频也用 resample。'W' 周、'M' 月、'Q' 季、'Y' 年。
③ rolling:开个窗口算均值
--------------------------------------------------------------------
7 天滑动平均:前 6 个是 NaN(窗口没满),第 7 个开始有值。
第 7 天 6372.86,最后一天 5713.14。
滑动平均把日波动抹平,趋势就露出来了。窗口越大越平滑,也越迟钝。
! rolling 的第一个非空位置是「窗口长度 - 1」。想让它一开始就有值,加 min_periods=1。
④ ohlc:一次给四个数
--------------------------------------------------------------------
| 日期 | open | high | low | close |
|---|---:|---:|---:|---:|
| 2026-01-04 00:00:00 | 7560 | 7729 | 5565 | 7729 |
| 2026-01-11 00:00:00 | 7138 | 10382 | 2075 | 4752 |
| 2026-01-18 00:00:00 | 2754 | 8592 | 2754 | 3450 |
open 是窗口第一天的值,close 是最后一天,high 和 low 是窗口内的最高最低。
这四个数就是 K 线要的四个数,一行代码凑齐。
! ohlc 要求索引已经是时间类型。字符串索引上去会直接报错。
⑤ 两个综合案例
--------------------------------------------------------------------
案例一:按周看金额趋势,找出最高的那一周。
最高的一周是 2026-02-22 那一周,合计 44637。
案例二:算每个月的总额和日均,比较月与月。
| 日期 | sum | mean |
|---|---:|---:|
| 2026-01-31 00:00:00 | 167835 | 5414.0 |
| 2026-02-28 00:00:00 | 171358 | 6119.9 |
| 2026-03-31 00:00:00 | 2668 | 2668.0 |
agg 里同时给 sum 和 mean,一次拿到月总额和月日均。
! resample 加 agg 是时间序列的万能组合。换频率、配统计,一句写完。
最后一行是关键:三月只有 1 天,所以它那 2668 不能和一月二月比。
resample('ME') 的 E 表示「月末」。老写法 'M' 在新版会警告或改变含义。
30 秒实验
- 把 rolling(7) 改成 rolling(30),看前 29 个是不是都空。
- 把 resample('W') 的标签改成周一('W-MON'),看第一周变成几天。
- 想一想:如果只拿三个月的数据做月度对比,应该怎么处理三月?
常见误区
- 随机数不固定种子:报告里的数别人复现不了。
- 把 'W' 当成周一起算:第一周会少几天,标签落在周日。
- 拿不完整月份做对比:三月只有一天,看着跌了 98%,其实只是没数据。
- 以为 rolling 从第一行就有值:窗口没满的期一律是 NaN。
自测三问(点开对答案)
1. resample('W') 的标签落在哪天? —— 落在每周的周日,也就是周末。第一周往往不满 7 天。 2. rolling(7) 的前 6 个为什么是 NaN? —— 窗口需要 7 个数才凑满,前 6 行凑不满,所以给空。加 min_periods=1 可以改。 3. 为什么三月的 2668 不能和一月比? —— 三月只覆盖了 1 天,一月覆盖 31 天。期间长度不同,总额没法直接比,得比日均。第 4 章十四篇到此结束。第 5 章开始换个工具:用 Matplotlib 把数据画出来。
- ☐ (本篇完,下一篇 5.1 图表的作用与怎么选)