简介:这套课程设计基于Python实现基本的地震易损性分析,集成了完整源代码与配套文档,面向土木工程、计算机、人工智能等专业的学生、教师及工程技术人员,既能用于课设与毕设,也适合作为Python工程分析的入门实战项目。项目压缩包共147个文件、约11.26MB,其中核心代码为6个Python脚本,16个eps与16个png图片呈现回归、残差及易损性曲线等分析结果,9个txt文档提供说明与备注,整体层次分明便于查找。目前已有80人学习浏览。代码均在测试运行成功后上传,曾作为毕业设计答辩项目并获答辩评审平均分96分;除源码外还提供了清晰的文档说明,可帮助理解地震易损性分析从数据处理、回归拟合到曲线绘制的完整流程。在此基础上可自行修改扩展,实现新的分析功能,也能作为课程设计或项目立项演示的可靠基础。
1. 为什么地震易损性分析要先看 ln 回归残差而不是直接画曲线?
拿到一个地震易损性分析的 Python 课程设计,打开源码目录大概率会看到这样的输出文件:ln(Pier1_cdrift) Regression.eps、ln(Bearing6_bdisp) Residual.eps、Abutment2_adispp FragilityCurve.eps。从文件名就能看出三层关系:先把构件位移或延性取对数做最小二乘回归,再检查残差是否随机,最后把回归参数带进对数正态累计分布函数才能画易损性曲线。这里的反直觉点在于,易损性曲线并不是“统计某次地震里多少结构损坏了”画出来的,而是基于一个简化的需求模型外推得到:ln(需求) = a + b * ln(地震动强度)。源代码里大量使用.eps矢量图,说明作者的目标不是只跑通程序,而是要把回归证据写进课程报告。下面直接盯住 ln 回归、残差检验、易损性曲线生成三个动作,适合刚完成 Python 基础语法、准备把结构易损性分析落地的学生,也适合想快速复现完整流程的工程师。
2. 双参数对数正态模型:易损性函数背后的回归依据
2.1 为什么需求预测要选幂函数而不是线性函数
桥梁构件的地震需求,比如墩柱曲率延性Pier1_cdrift、桥台位移Abutment2_adispp、支座位移Bearing6_bdisp,在低强度地震动下增长缓慢,在强震下进入非线性后响应迅速放大。直接做线性回归时,大震样本的离散度压过了小震样本,回归线会被大震点“带偏”。用一个幂函数表达式描述需求与地震动强度指标(IM)的关系:
D = a * IM^b * ε
两边取对数后变成:
ln(D) = ln(a) + b * ln(IM) + ln(ε)
如果ln(ε)服从正态分布,那么在ln(IM)-ln(D)空间里做最小二乘回归,残差就是对数空间里的预测误差。这样处理有两个直接好处:一是小震和大震的误差权重被拉平,残差更容易满足等方差假设;二是回归斜率 b 直接反映了构件需求对地震动强度的敏感性,桥墩、桥台、支座之间可以横向比较。源代码中文件名前面的ln(...)前缀,正是为了让读者意识到这张图的对数坐标系,而不是文件名随便起的。
2.2 从回归残差中提取标准差 β
用scipy.stats.linregress拟合直线能拿到斜率、截距、R² 和 p 值,但它不会直接返回残差标准差。残差标准差是后面易损性曲线斜率的关键参数,必须自己计算。设拟合值为:
ln(D_i_pred) = ln(a) + b * ln(IM_i)
残差为:
r_i = ln(D_i) - ln(D_i_pred)
所有残差的样本标准差记为 β_d,代表需求对数空间的随机不确定性。在结构工程文献里,这个 β_d 经常被直接叫做“回归标准差”或“对数标准差”。它决定了易损性曲线是陡峭还是平缓:β_d 越小,需求预测越精准,脆弱性曲线在特定能力阈值下就越“干脆”;β_d 越大,表示同样地震强度下响应可能差别很大,曲线斜率变缓。
除了需求侧的不确定性,构件能力本身也有离散性。工程上常用一个附加对数标准差 β_c 来描述,比如钢筋混凝土墩柱的曲率延性能力离散、桥台位移限值的模型误差。最终计算失效概率时,总标准差为:
β_tot = sqrt(β_d^2 + β_c^2)
在课程设计中,若没有能力试验数据,β_c 常被设为 0.2~0.4。源代码里如果直接只给了 Regression 图和 Residual 图,没有给能力标准差,一般按 0.3 处理是比较常见的选择。
2.3 能力阈值与失效概率公式的对数正态表达
构件失效条件定义为“需求大于能力”。当需求和能力都是对数正态随机变量时,给定 IM 下的失效概率可以写成标准正态累计分布函数:
P_fail(IM) = Φ( (ln(a) + b * ln(IM) - ln(C_m)) / β_tot )
其中C_m是构件能力的中位值。实际项目里,不同构件的C_m取值差距很大。下表列出三个典型构件的取值示例,具体数值应以源代码里的说明为准:
| 构件输出名 | 响应量 | 能力中位示例 | 说明 |
|---|---|---|---|
Pier1_cdrift | 墩柱曲率延性比 | 0.04 | 延性限值,查桥墩塑性铰模型 |
Abutment2_adispp | 桥台位移(m) | 0.05 | 桥台挡块或桩基容许位移 |
Bearing6_bdisp | 支座位移(m) | 0.10 | 常见板式橡胶支座剪切变形限值 |
注意如果C_m的单位和D不一致,比如位移取毫米、延性取无量纲比值,回归出来的ln(a)会整体偏移,易损性曲线中位值也会出错。起手的第一步应该是检查所有响应列的量纲统一,而不是先跑回归。
3. Python 实现:从时程响应数据到回归图、残差图和易损性曲线
3.1 数据结构与 CSV 组织
源代码没有给出原始时程数据,但一般课程设计中的数据表是csv或xlsx格式,按“每行一次地震动输入,每列一个响应量”排列。常见做法是这样:
| 工况 | IM | Pier1_cdrift | Abutment2_adispp | Bearing6_bdisp |
|---|---|---|---|---|
| GM001 | 0.12 | 0.0032 | 0.0051 | 0.0084 |
| GM002 | 0.25 | 0.0081 | 0.0104 | 0.0162 |
| GM003 | 0.51 | 0.0247 | 0.0233 | 0.0490 |
第一列是地震波编号,第二列是地震动强度指标IM,后面各列是目标构件的最大响应。如果需要做多个指标的对比分析,可以额外增加一列Sa_T1或PGA。读取时用 pandas 沿线解析即可。需要注意,某些工况可能在中震级别下构件没屈服,响应接近零,取对数会出现-inf或者很夸张的负值。应对策略是对小于某极小阈值的数据保留但标记,一般不要直接删掉,因为这会改变回归样本的分布。
3.2 用 scipy 做对数回归并计算残差标准差
下面这段代码完成核心的回归计算。为了便于理解,拆成数据读取、单构件拟合、结果输出三步:
import numpy as np import pandas as pd from scipy import stats # 读取时程响应记录 data = pd.read_csv("seismic_im_d.csv") im = data["IM"].to_numpy() # 选择单个构件做回归分析 y = data["Pier1_cdrift"].to_numpy() # 取对数:ln(IM) 与 ln(需求) x = np.log(im) ylog = np.log(y) # 最小二乘线性拟合 reg = stats.linregress(x, ylog) # 计算残差和回归标准差 y_pred = reg.intercept + reg.slope * x residual = ylog - y_pred beta = np.std(residual, ddof=2) print(f"slope(b) = {reg.slope:.4f}") print(f"intercept(ln a) = {reg.intercept:.4f}") print(f"R^2 = {reg.rvalue**2:.4f}") print(f"residual std beta_d = {beta:.4f}")逻辑说明:linregress接受两个等长数组,返回斜率slope、截距intercept、相关系数rvalue等。把IM和Pier1_cdrift分别取np.log,等价于在双对数空间做线性拟合。ddof=2表示计算残差标准差时减去两个自由度,这是小样本下对回归模型自由度损失的一种修正,课程报告里写这个细节会让答辩印象分提升。
参数说明:x是ln(IM),y是ln(Pier1_cdrift)。斜率b越接近 1.5,说明墩柱延性需求随着地震动强度增长而急剧放大;截距ln(a)是回归线在对数空间中的基准位置,受IM单位影响很大。这里的启发在于,若把IM从g改成cm/s²,截距会变化,但斜率不变;因此跨项目对比时一定要写清楚强度指标单位。
3.3 批量绘制 Regression 与 Residual 图,并输出 eps
课程设计里通常要处理多个构件,逐个手写重复代码太低效。下面用一个列表循环生成所有构件的回归图和残差图。代码里使用 Matplotlib 的savefig(..., format="eps")直接输出与源代码同名的.eps文件。
import matplotlib.pyplot as plt components = ["Pier1_cdrift", "Abutment2_adispp", "Bearing6_bdisp"] for comp in components: ylog = np.log(data[comp].to_numpy()) reg = stats.linregress(x, ylog) y_pred = reg.intercept + reg.slope * x residual = ylog - y_pred # 两个子图:左:回归直线,右:残差分布 fig, (ax_left, ax_right) = plt.subplots(1, 2, figsize=(9, 4)) ax_left.scatter(x, ylog, s=14, alpha=0.6, label="observed") x_dense = np.linspace(x.min(), x.max(), 200) ax_left.plot(x_dense, reg.intercept + reg.slope * x_dense, "--", color="red", label="ln regression") ax_left.set_xlabel("ln(IM)") ax_left.set_ylabel(f"ln({comp})") ax_left.legend() ax_right.scatter(x, residual, s=14, alpha=0.6) ax_right.axhline(0.0, color="black", linewidth=0.8) ax_right.set_xlabel("ln(IM)") ax_right.set_ylabel("residual") plt.tight_layout() plt.savefig(f"ln({comp})_Regression.eps", format="eps") plt.close(fig)这段代码把每一个构件的回归结果和残差图写到同一个figure里,导出为ln(Pier1_cdrift)_Regression.eps这种命名风格。x_dense用于画平滑的回归直线,避免只画一条连接两个端点的生硬线段。残差图里如果看到点分布不是水平的带状,而是开口喇叭形,说明对数变换还不够,可能需要考虑给IM换指数或分段拟合。
3.4 易损性曲线绘图
回归参数有了之后,画脆弱性曲线的核心就是把P_fail(IM)公式转成代码。以下代码给出对每个构件分别绘制的实现:
from scipy.stats import norm cap_map = { "Pier1_cdrift": 0.04, "Abutment2_adispp": 0.05, "Bearing6_bdisp": 0.10, } beta_c = 0.30 IM_range = np.geomspace(im.min(), im.max(), 300) ln_IM = np.log(IM_range) plt.figure(figsize=(7, 5)) for comp in components: ylog = np.log(data[comp].to_numpy()) reg = stats.linregress(x, ylog) y_pred = reg.intercept + reg.slope * x residual = ylog - y_pred beta_d = np.std(residual, ddof=2) beta_total = np.sqrt(beta_d**2 + beta_c**2) mu_lnD = reg.intercept + reg.slope * ln_IM capability = np.log(cap_map[comp]) pf = norm.cdf((mu_lnD - capability) / beta_total) plt.plot(IM_range, pf, lw=2, label=comp) plt.xscale("log") plt.xlabel("IM (g)") plt.ylabel("P(fail | IM)") plt.legend() plt.grid(True, linestyle="--", linewidth=0.4) plt.tight_layout() plt.savefig("FragilityCurve_All.eps", format="eps") plt.savefig("FragilityCurve_All.png", dpi=150)参数说明:np.geomspace在对数坐标上生成 300 个 IM 点,避免线性空间在小震区域取点太少。beta_total是需求残差标准差和能力标准差的平方和开方。norm.cdf的计算符号需要注意,漏掉负号会导致曲线递增变成递减;正确写法是需求超过能力时失效,即(mu_lnD - ln(C_m)) / beta_total增大则失效概率增大。如果最终曲线随 IM 增大反而下降,优先检查这个公式的正负号。
cap_map里的能力中位值不对时,整条曲线会在 IM 轴上左右平移,但不改变曲线形状。课程设计中如果直接复制网上的阈值,一定要结合源代码里的构件定义。比如某个桥墩已经算出塑性铰极限转角,就不能再沿用默认延性比 0.04。
4. 回归参数与 FragilityCurve 输出文件的对应关系、常见坑点排查
4.1 文件名中的 Regression、Residual、FragilityCurve 对应什么
源代码输出的.eps文件命名非常规则,看文件名就能判断分析状态。下面用表格整理实际映射关系:
| 输出文件 | 横轴 | 纵轴 | 告诉你什么 |
|---|---|---|---|
ln(Abutment2_adispp) Regression.eps | ln(IM) | ln(桥台位移) | 对数线性模型拟合是否成立 |
ln(Abutment2_adispp) Residual.eps | ln(IM) | 回归残差 | 残差是否存在趋势或异方差 |
Abutment2_adispp FragilityCurve.eps | IM | 失效概率 | 构件易损性的最终判定结果 |
ln(Pier1_cdrift) Regression.eps | ln(IM) | ln(曲率延性) | 墩柱响应放大速度 |
Pier1_cdrift FragilityCurve.eps | IM | 失效概率 | 墩柱倒塌或损伤曲线 |
如果某个构件只出现Regression.eps而没有FragilityCurve.eps,通常是因为缺少能力阈值C_m,或者该构件的响应数据存在大量零值,无法取对数。此时回到数据清洗,把零值替换成极小正数(如1e-5)再试,比直接把整条工况删掉要合理。
4.2 回归系数 a、b 和 β 的解读方式
以桥墩Pier1_cdrift为例,如果拟合出的结果是这样一组示意值:
| 构件 | b | ln(a) | β_d | R² |
|---|---|---|---|---|
| Pier1_cdrift | 1.48 | -2.96 | 0.32 | 0.86 |
| Abutment2_adispp | 1.06 | -3.71 | 0.25 | 0.74 |
| Bearing6_bdisp | 0.92 | -4.59 | 0.48 | 0.63 |
b=1.48说明墩柱延性需求对地震动强度很敏感,ln(a)=-2.96对应的是基准响应水平;Bearing6_bdisp的 β_d 达到 0.48,说明支座位移在相同地震强度下离散度更大,可能是摩擦滑移或挡块碰撞导致。这种情况下再去要求易损性曲线非常陡峭是不现实的。
桥梁抗震易损性分析里常见的误读是把 R² 当唯一质量标准。实际上R²=0.74的桥台位移模型也可以继续使用,因为需求预测模型不一定要求完全线性,只要残差分布无偏、β_d 稳定即可。反过来,如果 R² 很高但残差明显弯曲,回归线只是强制穿过数据中间,易损性曲线中段反而可能偏差很大。可以增加ln(IM)的平方项做多项式对比,观察残差是否有明显系统性弯曲,这是判断是否需要更高阶模型的简单办法。
4.3 阈值与 β_c 调整影响
同一个构件的能力中位值和能力标准差不同,易损性曲线差异很大。以Bearing6_bdisp为例,当能力中位C_m从 0.10 m 降到 0.06 m 时,中位失效概率对应的 IM 会明显左移;当 β_c 从 0.3 提高到 0.6,曲线斜率明显放缓。课程报告的结论部分应该写明“为什么采用该阈值”,常见依据是《公路桥梁抗震设计规范》中的支座容许位移,或者文献里同类桥型的能力模型。源代码如果没有给出阈值来源,建议在 README 或答辩幻灯片里补一句“能力参考自某规范/论文”,这比列出公式更容易让老师认可。
这里的实操检查方式是固定一组阈值,只调整 β_c,画出两组易损性曲线并观察差异。如果 β_c 从 0.2 变到 0.5,曲线变化仍然很小,说明需求侧不确定性占主导,后续优化重点应放在提高回归质量上;如果曲线变化剧烈,说明能力不确定度不可忽略,需要从材料本构或试验数据中取得更可靠的C_m。
4.4 运行环境与常见报错
在下载源代码后,最容易卡住的三个问题:eps文件打开为空白、中文乱码、scipy版本不兼容。.eps文件在 Windows 自带的图片查看器可能打不开,推荐用 Acrobat Reader 或 Inkscape 查看;如果是投稿论文,直接嵌入 LaTeX 更合适。matplotlib 保存.eps时默认字体是 Type 3,部分期刊会要求 Type 42 或 TrueType,可以用plt.rcParams['pdf.fonttype'] = 42和plt.rcParams['ps.fonttype'] = 42规避。另一个常见报错是numpy新版本对np.float的移除导致旧代码崩溃,需要把源码里的np.float替换为float。Python 环境搭建建议直接使用 Anaconda,安装代码为pip install pandas scipy matplotlib或conda install ...,可以顺手省去大量编译时间。
5. 批量构件易损性分析与小样本置信区间计算技巧
易损性曲线代码真正能继续深入的地方,是把“单条曲线”升级为“带置信区间的曲线”。由于地震波样本数通常只有 20~50 条,回归斜率、截距和残差标准差都有抽样误差。默认曲线是一条点估计,不能回答“到底有多可信”这个问题。比较轻量的是用 Bootstrap 重采样来评估中位值 IM_50 的不确定范围。
IM_50 是失效概率 0.5 对应的地震动强度,用回归参数可以显式写出:
IM_50 = exp( (ln(C_m) - ln(a)) / b )
这个公式很简单,却经常被忽略。它把易损性曲线的中位位置和回归参数直接挂钩:只要有回归系数和能力阈值,IM_50 就可以先算出来作为验证参照。如果从图里读出的 50% 概率点与公式算出的对不上,一定是在norm.cdf里正负号或者beta_total合并出了问题。
再看 Bootstrap 实现。对整个数据行做有放回抽样,每次抽样拟合一次回归参数,得到一条 IM_50 或指定 IM 下的失效概率,重复 500~1000 次,就能画出 5%~95% 置信带。核心代码如下:
rng = np.random.default_rng(42) n_bootstrap = 500 pf_matrix = np.zeros((n_bootstrap, len(IM_range))) for i in range(n_bootstrap): idx = rng.integers(0, len(data), size=len(data)) sample = data.iloc[idx] im_sample = np.log(sample["IM"].to_numpy()) y_sample = np.log(sample["Bearing6_bdisp"].to_numpy()) reg_i = stats.linregress(im_sample, y_sample) residual_i = y_sample - (reg_i.intercept + reg_i.slope * im_sample) beta_d_i = np.std(residual_i, ddof=2) beta_total_i = np.sqrt(beta_d_i**2 + beta_c**2) mu_i = reg_i.intercept + reg_i.slope * ln_IM pf_matrix[i] = norm.cdf((mu_i - np.log(cap_map["Bearing6_bdisp"])) / beta_total_i) pf_lower = np.percentile(pf_matrix, 5, axis=0) pf_upper = np.percentile(pf_matrix, 95, axis=0)逻辑说明:rng.integers生成随机行号,有放回抽样后重复拟合。pf_matrix每一行是一次重采样得到的完整易损性曲线,最后用percentile取出包络带。为什么不用直接对公式误差传递解析推导?因为b和β_d之间并不独立,Bootstrap 能够自然处理这种统计相关性。500 次重采样已经够课程设计展示趋势,1000 次以上更平滑。
这里有一个实际经验:如果重采样后的置信区间在 50% 失效概率处宽度超过一倍 IM 区间,说明地震波数量严重不足,需要扩展谱匹配的地震波库,而不是继续调节 β_c。反过来,如果置信带很窄,但回归残差图明显歪斜,说明样本数量够但模型形式有问题,此时应对比ln(IM)与IM两种自变量下的残差标准差,选择更稳定的一种。这个做法可以同时应付课程报告里的“不确定度分析”问题和答辩老师的提问,比单纯贴一张主曲线更有说服力。
本文还有配套的精品资源,点击获取