
简介《应用时间序列分析习题集答案》是一份面向统计学、计量经济学及数据分析方向学习者与备考人员的习题解答文档适合在完成课后练习、复习模型推导或核对计算结论时查阅。压缩包内仅含1个doc文件约1.35MB以文字解答与必要图表说明为主按章节顺序编排便于逐题对照目前已有280人学习下载。内容覆盖第二章至第六章从非平稳序列的时序图、自相关图与自相关系数计算到Ljung-Box白噪声检验再到AR、MA、ARMA、ARIMA模型的参数估计、平稳域与可逆性判别并给出不稳定性证明与Green函数递推过程后续章节还涉及时间序列预测、指数平滑法、移动平均法及两者比较、季节指数与趋势拟合、单位根检验等要点。解答中保留了分位点、P值、置信区间等关键数值可帮助读者核验手算过程、理清建模思路对课程作业和期末复习均有较直接的参考价值。1. 从 .doc 习题答案切入时间序列分析要连起来看的四类问题很多人拿到《应用时间序列分析习题集答案》这类 .doc 文档第一反应是逐题对数字。但 16 页翻下来真正值钱的不是那些小数点后四位的结果而是它把四类问题串成了一条完整链路序列平稳性怎么判、模型口径怎么定、参数估计与残差检验怎么落地、预测区间怎么算出来。第二章给的是时序图和样本自相关图第三章把 AR、MA、ARMA 的平稳域和可逆域摆开第四章讲移动平均与指数平滑第五章进入 ARIMA 和疏系数模型第六章收在单位根、协整和误差修正模型上。数字是结论判别规则才是可以迁移的东西。这份答案适合两类人赶作业时需要一个交叉验证基准的学生以及已经能用 Python 跑 ARIMA、但说不清为什么定这一阶的从业者。我的用法是先把每道题的判别条件抄成表再用 statsmodels 逐条复算对不上的那几处往往就是自己理解偏了的地方。2. 平稳性判别与样本自相关图第二章 2.1–2.6 的 Python 复算2.1 时序图和自相关图为什么要成对看单看时序图只能判断有没有趋势和周期判断不了这个非平稳是均值非平稳还是方差非平稳。单看自相关图能看出衰减速度但对周期成分的识别不如时序图直观。第二章前六题的编排逻辑就是逼你把两张图放在一起对照2.1 的时序图单调上升对应 ACF 缓慢线性衰减是典型的趋势型非平稳2.2 的 ACF 带着明显的正弦包络衰减说明序列同时含趋势和周期2.3 的 ACF 快速落到置信带内可以判平稳2.3 第三问的 ACF 几乎全部落在置信带内才叫白噪声。图形特征序列类型对应题号时序图单调上升ACF 线性缓降趋势型非平稳2.1、2.2ACF 正弦包络衰减周期加趋势非平稳2.2ACF 快速衰减至置信带内平稳非白噪声2.32ACF 几乎全部落在置信带内白噪声2.33时序图方差随时间放大方差非齐5.5、5.62.2 样本自相关系数的复算与置信带2.3 给出的前七阶样本自相关系数是 0.2023、0.013、0.042、-0.043、-0.179、-0.251、-0.094。判断它是不是白噪声光看数值大小不够得跟置信带比。常用做法是按近似正态性取 1.96 除以根号 nimport numpy as np from statsmodels.tsa.stattools import acf # 2.3 题给出的前 7 阶样本自相关系数 rho [0.2023, 0.013, 0.042, -0.043, -0.179, -0.251, -0.094] n 30 # 题目序列长度 band 1.96 / np.sqrt(n) # 95% 置信带的单侧宽度 print(round(band, 3), [abs(r) band for r in rho]) # 全部 False - 前 7 阶都落在置信带内配合时序图可判为白噪声 # 若手里是原始序列直接用 statsmodels 重算会额外给出 Bartlett 置信区间 acf_vals, confint acf(series, nlags20, alpha0.05, fftTrue) print(np.round(acf_vals[:8], 4)) print(np.round(confint[:8], 4))nlags控制输出的最大滞后阶数一般取到 n/4 左右alpha0.05决定置信区间宽度fftTrue在序列较长时用快速傅里叶变换加速几百个点以上才划算。判断标准是样本自相关系数的绝对值超过1.96/sqrt(n)的个数如果超过约 5%就不能当白噪声处理。2.3 的结论之所以是白噪声正因为这七个数一个都没越界。2.3 Ljung-Box 检验把 2.4 的结论翻译成阈值2.4 给的是 LB 统计量 4.83、分位点 0.9634、P 值 0.0363。这三个数是同一个东西的三种表达P 值等于 1 减去卡方分布的分位点而自由度得从题目拟合的模型阶数倒推。想验证这类数字是否自洽用反推法最直接from scipy import stats LB, p_target 4.83, 0.0363 # 反推自由度找到使 chi2.cdf(LB, df) 接近 1-p 的 df for df in range(1, 16): cdf stats.chi2.cdf(LB, df) if abs(cdf - (1 - p_target)) 0.02: print(候选自由度, df, 分位点, round(cdf, 4)) # 取定自由度后判定规则只有一条p 0.05 就拒绝纯随机假设 print(结论:, 非纯随机 if p_target 0.05 else 无法拒绝纯随机)stats.chi2.cdf返回的是左尾概率所以分位点对应1-p。语境的坑在于LB 统计量前面拟合过 ARMA 模型时自由度要减去被估参数个数论文里常见写法是m - p - q不减去就会把 P 值算大把本该判为非随机的序列放过去。2.4 的 P 值 0.0363 小于 0.05所以结论是序列不能视为纯随机序列也就是说还有信息可以挖。2.4 从 .doc 里把数字捞出来时容易踩的坑.doc 里公式常以 OLE 对象或域代码形式保存粘到 Markdown 里符号会丢数字之间还常常是连续空格而不是制表符。有些在线阅读器直接报无法预览 doc用 Word 或 WPS 另存为 .docx 之后再提取文本稳定性会好很多。拿到纯文本后读进 pandas 也别手敲import pandas as pd, io raw 0.2023 0.013 0.042 -0.043 -0.179 -0.251 -0.094 0.0248 -0.068 -0.072 0.014 0.109 0.217 0.316 df pd.read_csv(io.StringIO(raw), sepr\s, headerNone) print(df.values.ravel()[:10])sepr\s把连续空白当单一分隔符正好对付 .doc 复制出来的不等宽空格headerNone是因为原文没有表头。这样读进来的 28 个自相关系数可以直接跟第六章的预测表用同一套解析逻辑处理。3. AR、MA、ARMA 的平稳域与可逆域第三章口径判别的统一算法3.1 特征根判据平稳只看 AR可逆只看 MA第三章的核心只有一句话AR 部分决定平稳性MA 部分决定可逆性ARMA 两者都要查。判别方式是解特征方程但这里有个极容易混的约定问题。王燕那本教材用的是特征根 λ 形式平稳和可逆的条件都是|λ| 1而另一些教材和英文文献直接对延迟算子多项式Φ(B)0求根条件写成根在单位圆外。两种说法互为倒数关系用错了结论会完全反过来。模型平稳条件可逆条件AR(1)φ1 1恒可逆AR(2)φ1φ21φ2−φ11φ21恒可逆MA(1)恒平稳θ1 1MA(2)恒平稳特征根模均小于 1ARMA(p,q)AR 部分特征根模小于 1MA 部分特征根模小于 1用 λ 约定写判别函数系数按降幂喂给np.rootsimport numpy as np def ar_roots(phi): AR 特征方程: lambda^p - phi1*lambda^(p-1) - ... - phip 0 return np.roots([1.0] [-p for p in phi]) def ma_roots(theta): MA 特征方程: lambda^q theta1*lambda^(q-1) ... thetaq 0 return np.roots([1.0] list(theta)) def judge(roots): mods np.abs(roots) return np.round(mods, 4), bool(np.all(mods 1)) # 3.11(2)特征根 0.6、0.5模都小于 1 - 平稳 # 3.11(4)0.2569、-1.5569有一个模大于 1 - 不可逆 print(judge(np.array([0.6, 0.5]))) print(judge(np.array([0.2569, -1.5569])))np.roots的输入是多项式系数从最高次项开始写缺项补 0。注意 AR 方程的系数要取负号MA 方程不取这是两套公式的唯一差别写错这里会让 3.11 六组根的判定全部颠倒。3.2 3.11 的六组根逐条对照把这六组根过一遍能验证上面的约定是不是自洽小题特征根模结论11.3738、-0.87361.3738 1非平稳20.6、0.5均小于 1平稳30.45±0.2693i0.5244可逆40.2569、-1.55691.5569 1不可逆50.7 与 0.6均小于 1平稳且可逆60.4124、-1.2124 与 1.1有两项超 1非平稳、不可逆3那对共轭复根值得单独算一下实部平方加虚部平方再开方等于 0.5244小于 1所以按 λ 约定判可逆。很多人在这里卡住是因为拿延迟算子多项式的根去比算出来是 1.907看着大于 1 就以为不可逆其实是把两个约定混着用了。3.3 3.7–3.10 的等价变形ARMA 拆成 MA(∞)3.8 的解法 1 用的是 Green 函数对照系数解法 2 是直接展开多项式合并同类项。两种做法的底层都是同一个变换把 ARMA 模型写成无穷阶 MA 形式。Green 函数的递推关系是G_j ψ_j Σ φ_i * G_{j-i}写出来不到十行import numpy as np def green(phi, psi, n20): ARMA 的 Green 函数递推: G_j psi_j sum(phi_i * G_{j-i}) G [] for j in range(n): val psi[j] if j len(psi) else 0.0 for i, p in enumerate(phi, start1): if j - i 0: val p * G[j - i] G.append(val) return np.array(G) print(np.round(green([0.5], [1.0], n6), 4)) # AR(1)G_j 0.5^jphi是 AR 系数列表psi是 MA 系数列表n是要输出的 Green 函数长度。这个函数在 3.14、3.16 里都能复用3.14 要证明的递推式就是它的特例3.16 算预测方差时Var(e_h) σ² * Σ G_j²直接把这个数组平方求和即可。会写这个递推第三章后半段的证明题基本就不用死记了。3.4 平稳域的网格扫描验证3.4 到 3.6 是纯不等式推导判断 AR(2) 的参数组合落在不在平稳域内。想验证自己推出的区间对不对用穷举扫描最快def ar2_stationary(phi1, phi2): return (phi2 phi1 1) and (phi2 - phi1 1) and (abs(phi2) 1) # 把题目给出的参数表达式代进来扫描参数 c 的可行区间 ok [round(c, 3) for c in np.arange(-2, 2, 0.001) if ar2_stationary(1 c, -c)] print(ok[0], ok[-1]) # 区间的两个端点三个条件缺一不可φ1φ21和φ2−φ11管的是特征根不穿过单位圆|φ2|1管的是根的模不为 1。扫描的步长取 0.001 足够复现教材给的区间端点再细没意义因为不等式解出来本来就是开区间。4. 差分、定阶与置信区间ARIMA 建模流程里 3.17–3.20、5.5 的落点4.1 差分阶数怎么定ADF 加差分后 ACF 双确认3.10 的解法 2 演示了一个标准动作原序列的特征根等于 1说明是非平稳做一阶差分后均值方差为常数差分序列平稳。落到代码上就是先跑单位根检验再对差分序列重算自相关图import numpy as np, pandas as pd from statsmodels.tsa.stattools import adfuller def adf_report(x, nameseries): s adfuller(x, autolagAIC, regressionc) print(f{name}: ADF{s[0]:.3f} p{s[1]:.4f} 临界值{s[4]}) adf_report(series) # 原序列 adf_report(series.diff(1).dropna()) # 一阶差分regressionc表示只含常数项对应 6.1 里带漂移项平稳的判定如果时序图有明显斜率改用regressionctautolagAIC让程序自动选滞后阶。判定顺序是先看 p 值再看临界值表——当 ADF 统计量比 1% 临界值还小时才敢说在 1% 水平下平稳。5.5 和 5.6 用的对数变换加一阶差分组合本质是先修方差再修均值两步顺序不能颠倒先差分再取对数会把负值变成 NaN。4.2 定阶AIC 与 BIC 的网格搜索3.17 到 3.20 四道题给的模型口径分别是 AR(1)、AR(1)、MA(1)、ARMA(1,3)这些结论在同一份数据上用信息准则也能搜出来。做法是把 p、q 的限制范围扫一遍import warnings from statsmodels.tsa.arima.model import ARIMA warnings.filterwarnings(ignore) results [] for p in range(0, 4): for q in range(0, 4): try: m ARIMA(train, order(p, 1, q)).fit() results.append((p, q, m.aic, m.bic)) except Exception: continue best sorted(results, keylambda r: r[3])[:3] for p, q, aic, bic in best: print(fARIMA({p},1,{q}) AIC{aic:.2f} BIC{bic:.2f})必须用同一个训练集否则 AIC 之间不可比。BIC 对参数个数惩罚更重样本量上百时优先信 BIC样本只有几十个点时AIC 和 BIC 经常给出不同答案这时候回头看 ACF 和 PACF 的拖尾截尾特征更靠谱。3.20 判成 ARMA(1,3) 这种高阶模型单靠信息准则容易过拟合得配合残差白噪声检验一起看。4.3 预测区间3.16 和 3.19 的算法拆开3.16 给的置信区间是[3.8275, 16.1509]中间那串被省略的公式就是x̂ ± 1.96 * sqrt(Var(e_h))。AR 模型的预测方差随步长增加而累积因为误差项通过 Green 函数一层层传导下去。用 statsmodels 可以直接拿到res ARIMA(train, order(1, 1, 0)).fit() fc res.get_forecast(steps5) print(np.round(fc.predicted_mean.values, 4)) # 点预测 print(np.round(fc.conf_int(alpha0.05).values, 4)) # 95% 置信上下限alpha0.05换成 0.01 就得到 99% 区间宽度会明显变宽这是判读预测结果时必须交代清楚的一项。3.19 的 MA(1) 预测区间只有下一步有意义因为 MA 模型两步以外的预测值直接回到均值区间宽度却继续膨胀——这是 MA 和 AR 在长期预测上的核心差别书上画的两条收敛曲线就是这个意思。4.4 疏系数模型的拟合5.5 的处理顺序5.5 的路径是原序列非平稳对数变换消方差非齐一阶差分后拟合疏系数 AR(1,3)。疏系数意味着滞后 2 阶的系数被约束为 0statsmodels 里没有直接的接口用 OLS 手工构造设计矩阵最省事import numpy as np, pandas as pd, statsmodels.api as sm lx np.log(series) # 1) 对数变换压缩方差 dlx lx.diff(1).dropna() # 2) 一阶差分消除趋势 # 3) 只把 lag1、lag3 放进设计矩阵实现疏系数 AR(1,3) X pd.DataFrame({lag1: dlx.shift(1), lag3: dlx.shift(3)}).dropna() y dlx.loc[X.index] print(sm.OLS(y, X).fit().params)shift(n)把序列整体后移 n 期构造滞后项dropna()去掉因移位产生的空值这一步不做的话 OLS 会直接报错。拟合完记得对残差再跑一次 Ljung-Box疏系数模型最容易犯的错是把本该保留的中间隔系数硬砍掉结果残差里留下显著自相关。5. 指数平滑、Holt 与季节分解4.1–4.8 里那套答案不唯一的算法5.1 移动平均与一次指数平滑的递推实现4.3 要求对比移动平均法和指数平滑法题目给的结果是 11.79277。两种方法的差别在权重分配移动平均对窗口内各期等权指数平滑按S_t α*x_t (1-α)*S_{t-1}递推权重随期数指数衰减。手写一遍比调库更能看清 α 的作用import numpy as np def ses(x, alpha, s0None): 一次指数平滑s0 为初始值默认取第一期 s x[0] if s0 is None else s0 out [] for v in x: s alpha * v (1 - alpha) * s out.append(s) return np.array(out) def sma(x, k): k 期简单移动平均逐期滚动 return np.array([x[i:ik].mean() for i in range(len(x) - k 1)]) print(round(ses(x, alpha0.3)[-1], 5)) print(round(sma(x, k3)[-1], 5))alpha越小曲线越平滑、对突变越迟钝alpha越大越贴近原始序列但预测的稳定性下降。初始值s0的取法教材上通常给两种直接取第一期的值或者取前几期的均值。样本期数少的时候选哪种对结果影响能到小数点后一位所以 4.3 这类题答案不唯一是正常的别拿自己的结果硬跟答案对。5.2 Holt 两参数模型把水平项和趋势项分开平滑4.5 说序列是显著线性递增可以用线性方程也可以用 Holt 两参数法。Holt 的做法是把平滑拆成两层水平项l_t和趋势项b_t各配一个平滑系数预测时把趋势外推 h 步。def holt(x, alpha, beta, l0None, b0None): l x[0] if l0 is None else l0 b (x[1] - x[0]) if b0 is None else b0 # 初始趋势取首两期差 levels, trends [], [] for v in x: prev_l l l alpha * v (1 - alpha) * (l b) b beta * (l - prev_l) (1 - beta) * b levels.append(l) trends.append(b) return np.array(levels), np.array(trends) lv, tr holt(x, alpha0.6, beta0.3) h 3 print(lv[-1] h * tr[-1]) # 向前 h 步预测初始趋势取首两期之差是常见做法也可以用线性回归的斜率。beta取 0.3 以下时趋势项变化平缓适合噪声大的序列beta调大到 0.5 以上趋势项会跟着单期波动乱跳。4.6 那种非线性递增序列Holt 外推会持续线性增长误差随时间放大改用二次曲线或者阻尼趋势更合适。5.3 季节分解与 X114.7 的加法模型流程4.7 是整份文档里步骤最长的一道题五步走算季节指数、消除季节影响、拟合趋势、检查残差、回代预测。它给出的 12 个季节指数是 0.960722、0.912575、1.038169、1.064302、1.153627、1.116566、1.042920、0.984162、0.930947、0.938549、0.902281、0.955179这 12 个数加起来正好等于 12说明是按各月均值除以全年均值算出的比值型指数。月份季节指数月份季节指数10.96072271.04292020.91257580.98416231.03816990.93094741.064302100.93854951.153627110.90228161.116566120.955179复现时先做归一化校验再消除季节影响import numpy as np s_idx np.array([0.960722, 0.912575, 1.038169, 1.064302, 1.153627, 1.116566, 1.042920, 0.984162, 0.930947, 0.938549, 0.902281, 0.955179]) print(round(s_idx.sum(), 6)) # 12.0归一化校验通过 deseason series / np.resize(s_idx, len(series)) # 比值型指数直接做除法 t np.arange(len(deseason)) slope, intercept np.polyfit(t, deseason, 1) # 线性趋势斜率才有实义 print(round(slope, 4), round(intercept, 4)) # 回代趋势值乘回对应月份的季节指数 k len(series) forecast (slope * (k np.arange(12)) intercept) * s_idx print(np.round(forecast, 4))np.resize会把 12 个指数循环平铺到序列长度正好对上年度周期。np.polyfit返回的最高次系数在前一次拟合就是斜率在前、截距在后。原文特意注明该趋势模型截距无意义主要是斜率有意义因为这里的截距只是拟合起点的基线真正的长期递增速率由斜率反映报结论时别把截距当业务指标讲。X11 方法的思路是把趋势项和季节项做多轮迭代分离结果和上面这套手工分解会有差异两种结果都属于正常范围。5.4 4.8 的曲线选择stepar 与 expo4.8 说序列有曲线趋势但没有固定周期所以走曲线拟合或曲线指数平滑。判断走哪条路的办法很朴素把多项式回归和指数回归都跑一遍比 RMSE。import numpy as np from sklearn.metrics import mean_squared_error t np.arange(len(series)) poly np.poly1d(np.polyfit(t, series, 2)) # 二次曲线 loglin np.poly1d(np.polyfit(t, np.log(series), 1)) # 指数型 rmse_poly mean_squared_error(series, poly(t)) ** 0.5 rmse_exp mean_squared_error(series, np.exp(loglin(t))) ** 0.5 print(round(rmse_poly, 3), round(rmse_exp, 3))指数型拟合对原序列取对数之后变成线性回归预测时要用np.exp还原。算出 RMSE 之后别只看大小还要看残差有没有残留趋势——残差里还有明显上升段说明模型阶数不够加次数比重选模型类型更有效。6. 协整与误差修正模型的检验技巧从 6.2–6.4 抠出可复现的验证链6.1 单位根检验的几种结论6.1 用五个例子把单位根检验的结果分了类原序列不平稳但一阶差分平稳例 2.1、一阶与 12 步差分后平稳例 2.2、带漂移项平稳例 2.3、不带漂移项平稳例 2.4、带漂移项平稳或趋势平稳例 2.5。这些结论全靠 ADF 里的regression参数区分nc无常数无趋势c只含常数ct含常数和趋势。同一组数据换参数结论可能从不平稳变成带漂移项平稳所以报告结果时必须把参数写出来。6.2 协整检验与 ECM6.4 的两步法6.4 的流程是对数变换后一阶差分平稳说明两个序列都是 I(1)可以做协整检验。标准做法是 Engle-Granger 两步法第一步拟合长期均衡方程第二步对残差做 ADF。import statsmodels.api as sm from statsmodels.tsa.stattools import coint, adfuller # 第一步长期均衡回归 X sm.add_constant(x) resid sm.OLS(y, X).fit().resid # 第二步残差平稳性检验注意必须用 nc无常数 print(adfuller(resid, maxlag1, regressionnc)[1]) # 更省事的做法coint 直接给出 Engle-Granger 专用临界值 t_stat, p_val, crit coint(y, x) print(round(t_stat, 3), round(p_val, 4), crit)这里的坑很实在残差 ADF 不能用标准临界值表因为残差是从回归里估出来的分布变了。coint输出的crit才是对的临界值通常比标准表更负。构建误差修正模型时把长期方程的残差滞后一期作为解释变量加进差分回归d_y, d_x y.diff(1).dropna(), x.diff(1).dropna() ecm sm.OLS(d_y, sm.add_constant(pd.DataFrame({ d_x: d_x, ecm_lag1: resid.shift(1) }).dropna())).fit() print(ecm.params)ecm_lag1的系数应该在 -1 到 0 之间绝对值反映偏离长期均衡后的回调速度这个系数不显著就说明误差修正机制不成立协整关系要重新怀疑。6.3 一个具体技巧把 .doc 里的预测表回读做区间校验6.3 的预测表是不定期公布结果时最常见的格式观测号、预测值、标准误、95% 置信上下限。把它读成 DataFrame 之后可以做一次自洽性校验——点预测加减 1.96 倍标准误应该等于置信上下限import pandas as pd, io raw 49 70.7924 49.4194 -26.0678 167.6526 50 123.8358 69.8895 -13.1452 260.8167 51 195.0984 85.5968 27.3317 362.8651 df pd.read_csv(io.StringIO(raw), sepr\s, headerNone, names[obs, forecast, std_err, lower, upper]) df[lower_check] (df[forecast] - 1.96 * df[std_err]).round(3) df[upper_check] (df[forecast] 1.96 * df[std_err]).round(3) print(df[[obs, lower, lower_check, upper, upper_check]])拿第 49 期试算70.7924 减 1.96 乘 49.4194 等于约 -26.07跟表里的 -26.0678 一致加回去得到约 167.65跟表里的 167.6526 一致。三行都对得上说明这张表的置信区间用的就是正态近似。如果哪一行对不上多半是原文抄写时漏了位数或者是用 t 分布分位点算的——样本量小的时候这两种算法差得出来。6.3 的置信区间下界出现负数也很正常掠食者数量的分布本身不为负但正态近似不约束这一点报预测结果时可以额外截断到 0。本文还有配套的精品资源点击获取