简介:《应用时间序列分析习题集答案》是一份面向统计学、计量经济学及数据分析方向学习者与备考人员的习题解答文档,适合在完成课后练习、复习模型推导或核对计算结论时查阅。压缩包内仅含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.2 |
| ACF 正弦包络衰减 | 周期加趋势非平稳 | 2.2 |
| ACF 快速衰减至置信带内 | 平稳非白噪声 | 2.3(2) |
| ACF 几乎全部落在置信带内 | 白噪声 | 2.3(3) |
| 时序图方差随时间放大 | 方差非齐 | 5.5、5.6 |
2.2 样本自相关系数的复算与置信带
2.3 给出的前七阶样本自相关系数是 0.2023、0.013、0.042、-0.043、-0.179、-0.251、-0.094。判断它是不是白噪声,光看数值大小不够,得跟置信带比。常用做法是按近似正态性取 1.96 除以根号 n:
import 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, nlags=20, alpha=0.05, fft=True) print(np.round(acf_vals[:8], 4)) print(np.round(confint[:8], 4))nlags控制输出的最大滞后阶数,一般取到 n/4 左右;alpha=0.05决定置信区间宽度;fft=True在序列较长时用快速傅里叶变换加速,几百个点以上才划算。判断标准是:样本自相关系数的绝对值超过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), sep=r"\s+", header=None) print(df.values.ravel()[:10])sep=r"\s+"把连续空白当单一分隔符,正好对付 .doc 复制出来的不等宽空格;header=None是因为原文没有表头。这样读进来的 28 个自相关系数,可以直接跟第六章的预测表用同一套解析逻辑处理。
3. AR、MA、ARMA 的平稳域与可逆域:第三章口径判别的统一算法
3.1 特征根判据:平稳只看 AR,可逆只看 MA
第三章的核心只有一句话:AR 部分决定平稳性,MA 部分决定可逆性,ARMA 两者都要查。判别方式是解特征方程,但这里有个极容易混的约定问题。王燕那本教材用的是特征根 λ 形式,平稳和可逆的条件都是|λ| < 1;而另一些教材和英文文献直接对延迟算子多项式Φ(B)=0求根,条件写成"根在单位圆外"。两种说法互为倒数关系,用错了结论会完全反过来。
| 模型 | 平稳条件 | 可逆条件 |
|---|---|---|
| AR(1) | |φ1|< 1 | 恒可逆 |
| AR(2) | φ1+φ2<1,φ2−φ1<1,|φ2|<1 | 恒可逆 |
| MA(1) | 恒平稳 | |θ1|< 1 |
| MA(2) | 恒平稳 | 特征根模均小于 1 |
| ARMA(p,q) | AR 部分特征根模小于 1 | MA 部分特征根模小于 1 |
用 λ 约定写判别函数,系数按降幂喂给np.roots:
import 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 的六组根逐条对照
把这六组根过一遍,能验证上面的约定是不是自洽:
| 小题 | 特征根 | 模 | 结论 |
|---|---|---|---|
| (1) | 1.3738、-0.8736 | 1.3738 > 1 | 非平稳 |
| (2) | 0.6、0.5 | 均小于 1 | 平稳 |
| (3) | 0.45±0.2693i | 0.5244 | 可逆 |
| (4) | 0.2569、-1.5569 | 1.5569 > 1 | 不可逆 |
| (5) | 0.7 与 0.6 | 均小于 1 | 平稳且可逆 |
| (6) | 0.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, n=20): """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, start=1): if j - i >= 0: val += p * G[j - i] G.append(val) return np.array(G) print(np.round(green([0.5], [1.0], n=6), 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+φ2<1和φ2−φ1<1管的是特征根不穿过单位圆,|φ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, name="series"): s = adfuller(x, autolag="AIC", regression="c") print(f"{name}: ADF={s[0]:.3f} p={s[1]:.4f} 临界值={s[4]}") adf_report(series) # 原序列 adf_report(series.diff(1).dropna()) # 一阶差分regression="c"表示只含常数项,对应 6.1 里"带漂移项平稳"的判定;如果时序图有明显斜率,改用regression="ct";autolag="AIC"让程序自动选滞后阶。判定顺序是先看 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, key=lambda r: r[3])[:3] for p, q, aic, bic in best: print(f"ARIMA({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(steps=5) print(np.round(fc.predicted_mean.values, 4)) # 点预测 print(np.round(fc.conf_int(alpha=0.05).values, 4)) # 95% 置信上下限alpha=0.05换成 0.01 就得到 99% 区间,宽度会明显变宽,这是判读预测结果时必须交代清楚的一项。3.19 的 MA(1) 预测区间只有下一步有意义,因为 MA 模型两步以外的预测值直接回到均值,区间宽度却继续膨胀——这是 MA 和 AR 在长期预测上的核心差别,书上画的两条收敛曲线就是这个意思。
4.4 疏系数模型的拟合:5.5 的处理顺序
5.5 的路径是:原序列非平稳,对数变换消方差非齐,一阶差分后拟合疏系数 AR(1,3)。疏系数意味着滞后 2 阶的系数被约束为 0,statsmodels 里没有直接的接口,用 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, s0=None): """一次指数平滑,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:i+k].mean() for i in range(len(x) - k + 1)]) print(round(ses(x, alpha=0.3)[-1], 5)) print(round(sma(x, k=3)[-1], 5))alpha越小曲线越平滑、对突变越迟钝;alpha越大越贴近原始序列,但预测的稳定性下降。初始值s0的取法教材上通常给两种:直接取第一期的值,或者取前几期的均值。样本期数少的时候,选哪种对结果影响能到小数点后一位,所以 4.3 这类题"答案不唯一"是正常的,别拿自己的结果硬跟答案对。
5.2 Holt 两参数模型:把水平项和趋势项分开平滑
4.5 说序列是显著线性递增,可以用线性方程也可以用 Holt 两参数法。Holt 的做法是把平滑拆成两层:水平项l_t和趋势项b_t各配一个平滑系数,预测时把趋势外推 h 步。
def holt(x, alpha, beta, l0=None, b0=None): 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, alpha=0.6, beta=0.3) h = 3 print(lv[-1] + h * tr[-1]) # 向前 h 步预测初始趋势取首两期之差是常见做法,也可以用线性回归的斜率。beta取 0.3 以下时趋势项变化平缓,适合噪声大的序列;beta调大到 0.5 以上,趋势项会跟着单期波动乱跳。4.6 那种非线性递增序列,Holt 外推会持续线性增长,误差随时间放大,改用二次曲线或者阻尼趋势更合适。
5.3 季节分解与 X11:4.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,说明是按"各月均值除以全年均值"算出的比值型指数。
| 月份 | 季节指数 | 月份 | 季节指数 |
|---|---|---|---|
| 1 | 0.960722 | 7 | 1.042920 |
| 2 | 0.912575 | 8 | 0.984162 |
| 3 | 1.038169 | 9 | 0.930947 |
| 4 | 1.064302 | 10 | 0.938549 |
| 5 | 1.153627 | 11 | 0.902281 |
| 6 | 1.116566 | 12 | 0.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 与 expo
4.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 协整检验与 ECM:6.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, maxlag=1, regression="nc")[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), sep=r"\s+", header=None, 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。
本文还有配套的精品资源,点击获取