📊 第7章 数据统计分析案例
📄 一句话概括

时间序列先看两头:最高 12 月 192 万,最低 2 月 96 万,差整整一倍。 移动平均把单月起伏摊平,剩下的才是趋势;代价是两头缺值。 季节指数大于 100 是旺季、小于 100 是淡季,十二个数加起来正好 1200。

📝 本章和前面几章的关系

这一章最后一篇。前面把数据横向、纵向、成对地都看过了,现在把它按时间排好再看一遍。 数据还是那十二个月的销售额,单位万元。

一、先看清走势:两头在哪

2025 年十二个月:108、96、118、112、125、113、142、138、129、145、168、192。 全年 1586 万,月均 132.17 万。 最高 12 月 192 万,最低 2 月 96 万,正好差一倍。

一条线从 2 月爬到 12 月,中间 6 月掉了一下——这就是季节性的苗头。 先找最大最小和它们的月份,比一上来就套模型有效得多。

二、移动平均:把波动抹平

三期移动平均:这个月、上个月、下个月,三个数取平均。 2 月的值是 (108+96+118)/3 = 107.33,3 月是 (96+118+112)/3 = 108.67。 移动平均把「这一个月的起伏」摊到三个月里,剩下的才是趋势。

十二个月只能算出十个值,头尾各少一个。五期平均更平滑,但要少四个值。 窗口越长越平滑,代价是丢的数据越多。这是一笔必须付的账。

三、季节指数:每个月比平均高多少

算法很简单:这个月的销售额,除以月均,再乘 100。 12 月 192 除以 132.17 得 145.3,2 月 96 除以 132.17 得 72.6。 大于 100 是旺季,小于 100 是淡季。100 就是「正好等于月均」。

十二个指数加起来正好 1200,因为它们的平均必然是 100。 看这一列就知道一年里哪几个月该备货、哪几个月该收缩。

四、一年数据能算出什么、不能算出什么

严格讲,季节指数要三年以上才算得稳——一年只能看到一次,分不清是季节还是偶然。 这次算出来的其实是「月度占比」,叫它季节指数是行话,心里要有数。 想要更硬的结果,得把三年、五年的月度数据摞起来,算同月平均。

数据不够就说数据不够,别拿一年的数去预测三年后的某个月。

五、预测下一年 1 月

最简单的季节预测:预估明年总量,除以十二,再乘这个月的季节指数。 假设明年总量涨 10% 到 1745 万,1 月的指数是 81.7。 1745 除以 12 得 145.4,乘 81.7% 得 118.80 万元。

这个数只用了趋势和季节,没考虑促销、竞争、天气、政策。 它是个起点,不是答案。真实预测还要拿最近几个月的数据不断修正。

六、动手 Demo:时间序列分析:趋势与季节

十二个月的销售额,先看两头,再算三期移动平均,最后算季节指数。 末尾用季节指数做一个明年 1 月的粗预测。

import numpy as np

S25 = [108, 96, 118, 112, 125, 113, 142, 138, 129, 145, 168, 192]
print("合计 %d 万元,月均 %.2f 万元" % (sum(S25), np.mean(S25)))
print("最高 %d,最低 %d,相差 %.2f 倍"
      % (max(S25), min(S25), max(S25) / min(S25)))

ma3 = [(S25[i - 1] + S25[i] + S25[i + 1]) / 3.0 for i in range(1, 11)]
print("三期移动平均:%s" % [round(v, 2) for v in ma3])

avg = np.mean(S25)
idx = [v / avg * 100 for v in S25]
print("季节指数:%s" % [round(v, 1) for v in idx])
print("十二个指数加起来:%.1f" % sum(idx))

pred = sum(S25) * 1.10 / 12.0 * idx[0] / 100.0
print("明年总量涨 10%% 的话,1 月约 %.2f 万元" % pred)

实跑输出:

--------------------------------------------------------------------
时间序列分析:趋势与季节
--------------------------------------------------------------------

  ① 一条线先看清走势
  --------------------------------------------------------------------
  2025 年十二个月销售额:[108, 96, 118, 112, 125, 113, 142, 138, 129, 145, 168, 192] 万元。
  全年合计 1586 万元,月均 132.17 万元。
  最高 12 月 192 万元,最低 2 月 96 万元,最高是最低的 2.00 倍。
  先看最大最小和它们的月份。这一步就能看出「有没有季节性」的苗头。

  ② 移动平均:把波动抹平看趋势
  --------------------------------------------------------------------
  三期移动平均,从 2 月到 11 月,一共 10 个值。

| 月份 | 销售额 | 三期移动平均 |
|---|---:|---:|
| 2 月 | 96 | 107.33 |
| 3 月 | 118 | 108.67 |
| 4 月 | 112 | 118.33 |
| 5 月 | 125 | 116.67 |
| 6 月 | 113 | 126.67 |
| 7 月 | 142 | 131.00 |
| 8 月 | 138 | 136.33 |
| 9 月 | 129 | 137.33 |
| 10 月 | 145 | 147.33 |
| 11 月 | 168 | 168.33 |

  移动平均把「这一个月的起伏」摊到三个月里,剩下的才是趋势。
  窗口越长越平滑,但两端丢的数据也越多:三期丢两个月,五期丢四个月。
  ! 移动平均会让首尾缺值,这是它的代价,不是 bug。做预测时首尾那几个月是空着的。

  ③ 季节指数:每个月比平均高多少
  --------------------------------------------------------------------

| 月份 | 销售额 | 季节指数 |
|---|---:|---:|
| 1 月 | 108 | 81.7 |
| 2 月 | 96 | 72.6 |
| 3 月 | 118 | 89.3 |
| 4 月 | 112 | 84.7 |
| 5 月 | 125 | 94.6 |
| 6 月 | 113 | 85.5 |
| 7 月 | 142 | 107.4 |
| 8 月 | 138 | 104.4 |
| 9 月 | 129 | 97.6 |
| 10 月 | 145 | 109.7 |
| 11 月 | 168 | 127.1 |
| 12 月 | 192 | 145.3 |

  指数 100 就是「正好等于月均」。12 月 145.3,说明比月均高 45.3%。
  2 月 72.6,比月均低 27.4%。
  季节指数大于 100 是旺季,小于 100 是淡季。十二个数加起来正好 1200。
  ! 只有一年数据,这个指数其实是「月度占比」,严格讲要三年以上才敢叫季节指数。

  ④ 预测下一年 1 月
  --------------------------------------------------------------------
  最简单的季节预测:预估明年总量,乘以这个月的季节指数。
  假设明年总量涨 10%,到 1745 万元,那 1 月大约是 118.80 万元。
  公式一句话:全年总量除以十二,再乘这个月的季节指数。
  ! 这个预测只用了趋势和季节,没有考虑促销、竞争、天气。它是个起点,不是一个答案。

range(1, 11) 是 2 月到 11 月的位置。首尾两个月的移动平均是空的,因为凑不齐三个数。 打印里 10%% 写两个百分号,因为这条字符串本身在走 % 格式化。这是 Python 的老规矩。 十二个指数加起来必然是 1200,因为它就是每个月除以均值再乘 100。可以拿它当验算。

30 秒实验

  1. 把移动平均改成五期,看还剩几个值,和趋势线比一比哪个更平滑。
  2. 把「涨 10%」改成「跌 5%」,看 1 月的预测变成多少。
  3. 想一想:只有一年数据时,算出来的季节指数为什么不可靠?

常见误区

  1. 拿一年数据当季节规律:一年只见到一次,分不清季节和偶然。
  2. 忘了移动平均会缺值:首尾补 0 或者补原值都是错的。
  3. 把季节指数当百分比:145.3 是「月均的 1.453 倍」,不是「高了 145.3%」。
  4. 拿粗预测当承诺:它没考虑促销、竞争、天气,只是条参考线。
自测三问(点开对答案) 1. 移动平均为什么要丢首尾? —— 凑不齐一个完整窗口。三期平均在两端各缺一个,五期各缺两个。 2. 季节指数 145.3 是什么意思? —— 这个月的销售额是月均的 1.453 倍,也就是比月均高 45.3%。 3. 为什么十二个季节指数加起来是 1200? —— 它们是「每个月除以同一个均值」再乘 100,加起来自然是 100 乘 12。

第 7 章到此收尾。第 8 章换赛道:从「描述和推断」进到「用模型预测」,上 scikit-learn。

相关笔记