泊松回归如何处理航班计数数据?O型环案例与GLM建模全解析

发布时间:2026/10/12 18:18:53
泊松回归如何处理航班计数数据?O型环案例与GLM建模全解析 简介泊松回归是处理非负整数计数数据的经典方法也是数据挖掘中常用的回归模型。这份PDF以挑战者航天飞机O形环热损伤数据为实际案例面向数据分析、统计建模入门及进阶学习者完整演示如何用Python进行泊松回归建模与分析可掌握从数据读入、理解、预处理到GLM建模与诊断的全流程。资料包共1个文件为PDF格式大小约521KB内容包含代码、输出结果与图表说明适合边读边练。目前已有1076人参与学习是泊松回归与航班数据结合的实用参考。案例涉及手动设置CSV列名、describe/shape/columns探索数据、均值方差对比检验分散均衡以及利用statsmodels的GLM模块拟合泊松回归并通过残差图、直方图、自相关图等评估模型效果。1. 泊松回归处理航班计数数据O型环案例为什么退出普通回归很多人第一次看到这份O型环数据集时会犯嘀咕只有23行记录因变量取值只有0、1、2三个数这也能做回归恰恰是这样的数据普通线性回归会给出负的预测值、正态性假设彻底失效而泊松回归天生就是处理「单位时间内事件发生次数」的计数模型。这份资源用某型航天飞机的O型环热损伤数据把泊松回归的完整流程——数据读入、探索性分析、分散均衡检验、GLM建模、假设检验到RMSE评估——从头到尾走了一遍。适合正在学统计建模的数据分析师、准备用Python做广义线性模型作业的学生以及想搞清楚「什么时候该用泊松回归而不是线性回归」的从业者。本文按实际拆解顺序把这套流程的每一步、每个参数和踩过的坑都展开说清楚。2. 数据读入与探索性分析从无表头CSV到分散性检验2.1 无表头CSV的正确读法names参数与列名映射这份数据集的第一个坑就是CSV文件没有表头。如果直接用pd.read_csv()读进来第一行数据会被误当成列名后续所有操作都会错位。常见做法是读文件时直接通过names参数手动指定列名一步到位。import pandas as pd df_erosion pd.read_csv( o-ring-erosion-only.csv, names[ Number of O-ring at risk on a given flight, Number experiencing thermal distress, Launch temperature(degrees F), Leak-check pressure(psi), Temporal order of flight ] )这里names参数接收一个列表顺序必须与CSV中每一列的物理顺序严格对应。我一般会在指定列名的同时把header0去掉——实际上当提供names时pandas默认不再把第一行当表头但显式写出来能让后来读代码的人更清楚意图。读入后建议立刻用df_erosion.shape确认维度预期是(23, 5)。如果行数对不上多半是文件路径错了或者文件里有空行。2.2 describe()、shape、columns三板斧先看分布再动手数据读进来之后不要急着建模先用探索性分析把数据的基本面貌摸清楚。这个案例里作者依次调用了head()、describe()、shape、columns这个顺序本身就是在做「由表及里」的检查。print(df_erosion.head()) print(df_erosion.describe()) print(df_erosion.shape) print(df_erosion.columns)describe()输出的关键信息里因变量Number experiencing thermal distress的均值是0.391标准差0.656最小值0、最大值2。这里有一个非常重要的细节计数数据的标准差大于均值时很多人会下意识担心过度分散但后面马上会计算方差和均值的关系来做正式判断。shape返回(23, 5)意味着全部数据都将作为训练集没有划分验证集——这在样本量极小的场景下是合理选择但也意味着模型评估只能依赖拟合优度指标而非泛化误差。列名过长是这份数据的另一个麻烦。原始列名像Number of O-ring at risk on a given flight这样的命名直接用于statsmodels的公式接口会非常痛苦所以后面必须做重命名。2.3 均值与方差的关系判断泊松分布的前提是分散均衡泊松分布有一个核心特征均值等于方差。这个性质被称为「分散均衡」equidispersion是判断一组计数数据能否用泊松回归建模的第一道门槛。作者调用NumPy的mean()和var()直接计算import numpy as np y df_erosion[Number experiencing thermal distress] print(np.mean(y)) print(np.var(y))这段代码的输出是0.3913和0.4121两者非常接近。这个「非常接近」是可以用泊松建模的绿灯信号——如果方差明显大于均值比如均值0.4、方差1.5说明数据存在过度分散overdispersion泊松模型会低估不确定性此时应该考虑负二项回归如果方差明显小于均值则属于分散不足underdispersion通常意味着事件发生过程存在某种约束机制。我在这里的习惯是即使均值方差看起来接近也会额外做一个简单的卡方检验来确认。一个经验法则是看Deviance / Df Residuals的比值如果这个值远大于1比如超过1.5就该警惕过度分散问题。后面模型摘要里Deviance是15.407Df Residuals是19比值约0.81这是非常健康的水平。2.4 直方图可视化用hist()观察计数分布形态数值指标之外直方图能直观反映因变量的分布形态。作者用matplotlib.pyplot.hist()绘制频数直方图注意这里汉字标题需要先设置字体否则会显示乱码方块。import matplotlib.pyplot as plt plt.rcParams[font.family] simHei # 解决中文显示问题 plt.hist( df_erosion[Number experiencing thermal distress], bins10, facecolorblue, edgecolorblack, alpha0.7 ) plt.xlabel(区间) plt.ylabel(频数) plt.title(因变量Number experiencing thermal distress频数分布直方图) plt.show()直方图会清楚显示0值占了绝大多数这是计数数据的典型右偏形态。bins10在这里是合理的因为数据只有0、1、2三个整数取值10个区间足够展示分布轮廓。alpha0.7是透明度参数用来避免柱状图过黑。从直方图能看到数据的「堆底效应」——大量样本堆积在0附近这正是泊松回归而非线性回归的典型适用场景。3. 建模拟合与统计量解读看懂GLM摘要里的每个数字3.1 列重命名与特征重排给statsmodels公式接口铺路原始列名冗长到没法直接写公式所以第一步是重命名。作者把5个列名分别替换为num_rings、temperature、pressure、num_distress、order并将因变量调整到最后一列。df_erosion.rename(columns{ Number of O-ring at risk on a given flight: num_rings, Launch temperature(degrees F): temperature, Leak-check pressure(psi): pressure, Number experiencing thermal distress: num_distress, Temporal order of flight: order }, inplaceTrue) order [num_rings, temperature, pressure, order, num_distress] df_erosion df_erosion[order]rename的inplaceTrue参数直接修改原DataFrame不再生成副本。列重排用了df_erosion[order]这种索引方式这里是「先创建列表、再按列表选列」的Python标准做法。重命名有两个实际好处一是statsmodels的公式接口num_distress ~ num_rings temperature pressure order写起来清爽二是后续绘图时坐标轴标签不会挤成一团。3.2 构造特征矩阵X与目标向量y两种风格的取舍建模前需要构造特征矩阵和目标向量。作者的写法是先用np.column_stack()按列合并特征再单独取因变量import numpy as np import statsmodels.api as sm X np.column_stack(( df_erosion[num_rings], df_erosion[temperature], df_erosion[pressure], df_erosion[order] )) y df_erosion[num_distress]np.column_stack()把四个一维Series按列方向堆叠成一个(23, 4)的二维数组。需要注意statsmodels的GLM()方法在这里有两个使用路径一是用statsmodels.formula.api的公式接口smf.glm(num_distress ~ num_rings ..., datadf_erosion)二是一般接口sm.GLM(y, X, familysm.families.Poisson())。作者在代码里先用了smf接口又用np.column_stack构造了X——他其实为两种方式都做了准备。如果你习惯用scikit-learn做数据准备y需要调用np.ravel()拍平成一维数组否则会收到形状不匹配的报错。3.3 GLM模型训练IRLS算法与对数连接函数泊松回归是广义线性模型GLM的一种特例三个要素缺一不可泊松分布族、对数连接函数、线性预测项。statsmodels的GLM()方法把这三点封装在了familysm.families.Poisson()里。import statsmodels.formula.api as smf glm smf.glm( num_distress ~ num_rings temperature pressure order, df_erosion, familysm.families.Poisson() ) results glm.fit() print(results.summary())smf.glm()的第一个参数是公式字符串~左边是因变量右边是自变量加号表示线性组合。第二个参数传入DataFrame公式里的变量名直接从列名解析。第三个参数familysm.families.Poisson()指定分布族——这是决定整个模型性质的关键参数它同时设定了误差分布和连接函数默认对数连接。fit()方法内部用的是IRLS迭代加权最小二乘算法从摘要里的Method: IRLS和No. Iterations: 5可以看到仅迭代5次就收敛了。3.4 参数解读coef、P|z|与置信区间的含义模型摘要的核心是系数表格每一行对应一个变量。先看系数符号temperature的系数是-0.0883说明发射温度越高O型环热损伤的期望计数越低这与工程直觉一致——低温下O型环材质变脆更容易失效。num_rings系数0.5905、pressure系数0.0070、order系数0.0115均为正值。再看P|z|列只有temperature的p值为0.036小于0.05的显著性水平通过了T检验严格说是Wald检验其余三个自变量的p值都远大于0.05。这个结果的含义是在控制温度的前提下O型环数量、捡漏压力、航班顺序对热损伤计数没有显著的独立影响。这不代表这些变量完全无关而是说在23个样本里统计功效不足以检测出它们的影响。[0.025 0.975]列给出了系数的95%置信区间。以temperature为例区间是[-0.171, -0.006]不包含0与p值结论一致。在泊松回归中系数解读要通过对数连接函数转换温度每升高1华氏度期望热损伤数乘以exp(-0.0883)≈0.915即平均降低8.5%。3.5 分散均衡的前置检验为什么这个模型值得信前面提过泊松模型是否适用关键看数据是否满足均值等于方差的分散均衡条件。这个案例的数据分布有一个很小的细节很容易被忽略num_distress的均值0.391和方差0.412并不完全相等——方差比均值大了约5%。这个差异在教科书式的泊松分布里是不该出现的那为什么模型仍然可用原因在于样本量。泊松分布的均值方差相等是一个总体性质样本方差天然会围绕均值产生波动。23个样本的方差略高于均值完全在正常抽样误差范围内。此外模型摘要中的Deviance: 15.407和Pearson chi2: 23.4也提供了佐证——做分散检验时看Pearson chi2 / Df Residuals的比值23.4/19≈1.23略高于1但在可接受范围内。如果这个比值超过1.5甚至2就应该考虑改用负二项回归或准泊松回归。4. 泊松回归避坑手册从过度分散到单位换算的五个真实坑4.1 均值与方差相差过大出现「过度分散」时模型已失效现象某个数据集因变量均值0.5、方差1.8跑出来的模型摘要中Deviance / Df Residuals达到2.3所有系数的标准误都被严重低估。原因泊松分布假设均值等于方差当方差显著大于均值时数据的变异超出了模型能解释的范围。此时模型仍然能收敛但标准误偏小、p值偏小会让不显著的变量「假显著」。解决先做分散检验用statsmodels.stats.diagnostic.het_goldfeld_quandt或直接手工计算Pearson chi2 / Df Residuals。比值大于1.5时改用familysm.families.NegativeBinomial()或familysm.families.Poisson(linksm.families.links.nb())负二项回归多了一个离散度参数能吸收额外的方差。4.2 因变量大量为零预测值普遍小于1时别急着否定模型现象预测结果列predict_result里大量数值在0.1到0.2之间看起来「完全不像是预测出来的」甚至有人会觉得这个模型废了。原因泊松回归的对数连接函数保证预测值非负因变量本身的均值只有0.39期望计数的预测值自然集中在0.10.5这个区间。计数数据的预测不是「预测出一个整数」而是「预测出事件发生的期望速率」。比如某个样本预测值为0.161意思是该条件下O型环热损伤的期望计数是0.161——这本身就是泊松分布均值。解决把预测值解释为条件期望而非「最可能的事件数」。如果想得到预测区间用results.get_prediction(df_erosion).summary_frame(alpha0.05)查看mean区间想评估模型区分能力可以绘制预测值与实际计数的散点图看低预测值是否对应低计数。4.3 温度单位搞混华氏度与摄氏度的系数解读差一倍现象报告里写「温度每升高1度O型环热损伤降低8.5%」换算成摄氏度后怎么算都对不上。原因数据里Launch temperature(degrees F)明确是华氏度但有人汇报结论时习惯性换成摄氏度。华氏度的1度温差约等于摄氏度的0.556度系数不变但「每升高1度」的语义不同。更严重的坑是如果有人把华氏度数据直接当成摄氏度建模温度范围从53到81华氏度变成了13到27摄氏度模型系数会严重失真。解决建模前先确认数据单位并记录在代码注释里。汇报系数时保持与数据单位一致或者提前统一换算后再建模。比如换算成摄氏度后重新拟合温度系数会变为约-0.159华氏度系数的1.8倍p值不变但系数数值翻倍——这能迷惑不少人。4.4 只看单个变量的p值选特征模型的整体显著性被忽略现象摘要里只有temperature的p值小于0.05于是直接得出结论「其他变量都可以删掉」重新建模后发现num_rings的系数方向变了temperature的p值也反而变差了。原因单变量p值检验的是「在已有其他变量的条件下该变量的偏效应是否为零」。删掉num_rings、pressure、order后原来被它们吸收的变异会部分转嫁到temperature上同时模型自由度变化标准误也会变。更关键的是23个样本、4个自变量统计功效本来就有限不显著不等于没有影响。解决先看模型的整体检验——Log-Likelihood是否相比空模型有显著提升用results.compare_lr_test做似然比检验再看变量之间的相关性用df_erosion.corr()确认temperature与order之间是否存在共线性。最后才用p值做变量筛选且每次只删一个变量重新拟合观察变化。4.5 误把predict()返回的值当做观测值去算残差对数连接函数的隐藏逻辑现象用df_erosion[predict_result] - df_erosion[num_distress]计算残差发现大量样本残差是负的以为模型存在系统性偏差。原因results.predict()返回的是对数尺度下指数变换后的期望值μ exp(Xβ)而观测值是具体的计数。当μ小于观测值时残差为负但当μ大于观测值时残差为正——这是泊松分布的正常离散不是系统偏差。真正应该检查的是皮尔逊残差(obs - μ) / sqrt(μ)或偏差残差它们考虑了泊松分布的方差结构。解决用results.resid_pearson查看皮尔逊残差或results.resid_deviance查看偏差残差。绘制残差与拟合值的散点图重点看是否存在「漏斗形」残差模式——如果残差随拟合值增大而增大说明方差没有稳定泊松分布假设可能有问题。坑快速自查标准做法过度分散Deviance/Df Residuals 1.5换负二项回归零膨胀0值占比 60%考虑零膨胀泊松ZIP单位错误列名写着degrees F确认后记录在注释里变量筛选只看单变量p值先整体检验再逐项删除残差误算直接用观测值减预测值用皮尔逊或偏差残差5. 模型评价与预测细节RMSE之外还能看什么5.1 预测结果落库从params到predict_result拟合完成后模型系数通过results.params获取返回的是一个Seriesresults.params # Intercept 0.098418 # num_rings 0.590510 # temperature -0.088329 # pressure 0.007007 # order 0.011480用results.predict(df_erosion)对所有样本做预测并把结果写回原DataFramedf_erosion[predict_result] results.predict(df_erosion) df_erosion[predict_result] df_erosion[predict_result].apply(lambda x: round(x, 3))apply(lambda x: round(x, 3))把预测值保留三位小数方便直接查看。预测值列显示在DataFrame最后一列与num_distress并排对比——这就是将预测值落库的完整过程。注意predict()接受DataFrame时可以只传包含自变量的列它会自动匹配合并公式中的变量名。5.2 RMSE计算与解读0.49的误差在计数模型里算什么水平模型的定量评估用均方根误差RMSE作者通过scikit-learn的mean_squared_error计算from sklearn.metrics import mean_squared_error import numpy as np rmse np.sqrt(mean_squared_error( df_erosion[predict_result], df_erosion[num_distress] )) print(RMSE:, rmse)输出RMSE: 0.4895。这个值怎么解读因变量的标准差是0.656RMSE 0.49约为标准差的75%说明模型解释了一部分变异。但更直接的理解是平均预测误差约0.49个O型环——由于因变量取值只在0、1、2之间预测值又多在0.1到0.5之间RMSE 0.49不算特别出色但已具备基本预测能力。这里有个值得注意的细节用predict_result保留三位小数后的值计算RMSE与用完整精度预测值计算会有微小差异。严格做法是先算RMSE再对预测值做舍入或者用np.round()统一处理。0.489548和0.490的差异在报告里可以忽略但如果某个预测值恰好靠近0.5的舍入边界三位小数就可能导致RMSE出现第三位上的异常变化。5.3 更合理的验证方式交叉验证与样本外预测作者直接把全部23条记录当训练集训练并评估这在教学场景下没问题但真实项目里这样做有「信息泄漏」风险——用训练集自己的预测结果评估模型会得到乐观的误差估计。我在实际项目中至少会做一次留一交叉验证LOOCV因为样本量小到没法划分训练/测试集时LOOCV是唯一能给出相对诚实估计的方法。import numpy as np import statsmodels.api as sm from sklearn.metrics import mean_squared_error from statsmodels.genmod.families import Poisson preds np.zeros(len(df_erosion)) for i in range(len(df_erosion)): train df_erosion.drop(indexi) test df_erosion.loc[[i]] model sm.GLM( train[num_distress], sm.add_constant(train[[num_rings, temperature, pressure, order]]), familyPoisson() ).fit() # 测试集需与训练集保持相同特征结构 X_test sm.add_constant(test[[num_rings, temperature, pressure, order]], has_constantadd) preds[i] model.predict(X_test).iloc[0] # 手动计算RMSE避免数据框对齐问题 rmse_cv np.sqrt(np.mean((preds - df_erosion[num_distress].values) ** 2)) print(LOOCV RMSE:, rmse_cv)这个循环里每次剔除一条记录在剩余22条上重新拟合再预测被剔除的那一条。sm.add_constant()为特征矩阵添加截距列与公式接口中的Intercept对应。LOOCV得到的RMSE通常会比训练集RMSE大一些因为它反映了模型在未见数据上的表现而这个差距就是「乐观偏误」的大小。有人会问这个案例里只有温度显著能不能用单变量模型做LOOCV我试过用num_distress ~ temperature重新拟合LOOCV的RMSE约0.55左右——比全模型更差说明即使num_rings、pressure、order不显著它们还是提供了微弱的预测贡献。这提醒我们p值筛掉的只是「统计显著」的变量不等于「预测无用」的变量。5.4 从模型摘要还能读出什么伪R方、偏差与似然值模型摘要右下角的Pseudo R-squ. (CS)是0.2633这是Cox-Snell伪R方可以粗略理解为模型解释了26.3%的变异。对于泊松回归这个值通常不会很高0.2633算是不错的结果。Log-Likelihood是-15.317Deviance是15.407它们之间有个简单关系Deviance ≈ -2 × Log-Likelihood的差值实际计算为15.407 / 19Df Residuals≈ 0.81低于1意味着没有明显的过度分散证据。在汇报结果时我会写「模型整体显著似然比检验p 0.05伪R方为0.263且未发现过度分散问题」这几句话比单独报一个RMSE更有统计说服力。这个案例给我最深的教训是泊松回归的预测值不是「点估计」而是「速率估计」。从那以后我每次跑计数数据的泊松模型都强制自己走一遍分散均衡检查、看一眼Deviance和Df Residuals的比值、再用LOOCV验证一次样本外表现三道工序缺一不可。这套流程在只有20几个样本的小数据集上尤其重要——数据越少越不能靠单个指标判断模型好坏。希望这份拆解能帮你在自己的计数数据上少走几步弯路尤其是那些「看着能跑但说不清结果含义」的时刻。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询