简介:本资源是面向地球物理学、地震学研究者及MATLAB初学者的ZMAP地震分析工具包,聚焦b值计算这一核心地震参数建模任务。资源完整封装了ZMAP软件全部功能模块,包含1670个MATLAB脚本(如zmap.m主程序、ini_zmap.m初始化文件、startZmap.m启动脚本)、22个地理数据文件(world_coastline.mat等海岸线数据)、以及辅助可视化素材(183个GIF、83个JPG、33个PNG),总文件数2240个,压缩包大小19.51MB。已有1131人学习下载,适用于区域地震目录统计分析、b值时空演化研究、地震危险性评估等科研与教学场景。用户可直接运行脚本完成震级-频度拟合、生成标准结果图表,并借助配套地理数据实现带底图的震中分布可视化;内容预览显示含gshhs_l.b等全球高分辨率海岸线二进制数据及bootslickw系列跨平台可执行工具,显著提升多系统兼容性与分析效率。 搞地震活动性研究的,几乎没有人能绕过b值这个概念,也很少有人能绕过ZMAP这套工具箱。我在做区域地震目录分析的时候,导师丢过来一句话:“把这个目录的b值扫一下,看看空间分布有没有异常。”当时我第一反应是——b值不是拟合一条直线就行了吗?自己写脚本不就行了?后来发现事情没那么简单。当你需要系统性地估计最小完整性震级、做空间网格扫描、评估b值误差的时候,手动脚本很快就变得难以维护和复现。ZMAP(ZMap)是Stefan Wiemer等人基于MATLAB开发的一套开源地震学分析工具箱,b值计算是它最核心、最成熟的功能之一。这篇文章就把我从下载zmap.zip到环境配置、数据整理、出图、再到被审稿人追问误差的完整过程,拆开讲清楚。内容覆盖新手需要的大多数疑问,也包含一些长期使用才能踩到的坑。
1. b值为什么重要:从震级-频度关系的“斜率”说起
1.1 古登堡-里克特定律:b值就是那条直线的斜率
地震学里有一句老话:小震不断,大震少来。这句话的定量表达就是古登堡-里克特(Gutenberg-Richter)关系式:
log10(N) = a - bM
其中N表示震级大于等于M的地震数量,a是地震活动水平的截距,b是斜率。绝大多数地区的b值在0.8到1.2之间,意味着震级每增加1级,频度大约变为原来的十分之一。b值做大之后,图上看就是一条向下倾斜的直线,而b值就是这条线的斜率。
但b值的意义远不止“拟合一条直线”。b值反映的是大小地震的比例关系,它和地下应力状态、介质性质有统计上的关联。很多研究指出,高应力区、破裂强度高的区域往往表现出低b值;而b值升高,则可能对应裂隙发育、流体活动增强或者应力释放。所以,b值的空间扫描被用来识别断层闭锁段、估计地震危险性,时间扫描则被用来追踪某个区域应力状态的演化。
不过需要注意,b值是一个统计量,不是某个具体地震的物理预报指标。看到某个区域b值降到0.6,不能简单推断“马上要大地震了”,但可以有理由认为这个区域的应力环境存在异常,值得进一步关注。这也是为什么审稿人一看到b值变化图,第一反应就是问“误差条呢?样本量多少?Mc选了多少?”
1.2 为什么选ZMAP:自己写脚本 vs 现成工具
最开始我也尝试自己写b值计算脚本。单区域拟合一条G-R曲线确实不难,三五十行MATLAB代码就能搞定。问题是,真实研究里你很快会遇到这些需求:
- 用最大曲率法、拟合优度法等方法估计最小完整性震级Mc;
- 把震级-频度分布中的非累积曲线和累积曲线叠加画在一起;
- 把研究区划分成网格,在每个节点上取最近N个事件计算b值,最后画成空间分布图;
- 用滑动时间窗口追踪b值变化,并叠加标准误差;
- 用Bootstrap方法评估b值的不确定性,给论文配上置信区间。
这些功能如果全部自己写,工作量会翻好几倍,而且极易出现统计口径不一致的bug。ZMAP把这些功能做了封装,虽然它本质上还是一堆MATLAB函数,但我们可以直接通过菜单或者调用函数完成上述分析。更重要的是,ZMAP在设计上遵循了地震学界的通用统计方法,用它计算的结果在同行评审时更容易被接受。
当然,Python生态里也有不少地震目录分析工具,但ZMAP在地震学界的积累更久、引用更广,很多经典文献里的b值图都是用ZMAP做出来的。对于刚入门的同行,先学会ZMAP再研究底层统计,是一条比较顺的学习路径。
2. 一个zip包,三分钟启动ZMAP:环境准备与版本选择
2.1 先检查MATLAB版本,避免装完就跑不起来
ZMAP的发布形式一直是压缩包,你搜到的“zmap.zip”大概率是某个版本的分发包。这里第一个坑就是版本匹配。
ZMAP老版本(比如6.0及更早)是在MATLAB还保留很多旧绘图函数的年代写的。后来MATLAB移除了plotyy、colorbar('v6')这类老式接口,老的ZMAP跑起来就可能报错。我试过在R2021a上直接打开老版本ZMAP,结果启动窗口起来之后,一旦画图就提示plotyy was removed,当场卡住。
我的建议是:
- 如果你手头的MATLAB是R2016a到R2020b之间的版本,老版ZMAP大概率能跑,但也要看具体的函数改动;
- 如果你的MATLAB是R2021a之后的新版本,尽量选择ZMAP 7.0以上的版本,作者已经针对新MATLAB做了一轮兼容性适配;
- 如果实验室还有旧版MATLAB,别急着升级,ZMAP这种研究工具跑得稳比版本新更重要。
检查你的MATLAB版本很简单,命令窗口输入version就能看到。建议在安装ZMAP之前先确认版本,再决定下载哪个分发包,不然解压之后跑不起来,浪费半天时间。
2.2 解压、路径设置与启动
拿到zip包之后的步骤不复杂,但每一步都有细节。
- 把压缩包解压到一个路径中不包含中文、不包含空格的目录,比如
D:\tools\zmap或者/home/user/tools/ZMAP。 - 打开MATLAB,把当前目录切换到你解压出来的ZMAP主目录。
- 在命令窗口执行路径添加命令:
addpath(genpath('D:\tools\zmap')); savepath;这里genpath会把ZMAP目录下所有子文件夹都加入路径,这一步很关键。ZMAP不是单文件工具,它引用了大量子目录里的函数,少一个文件夹都可能出现Undefined function。savepath是为了把路径保存下来,下次启动MATLAB不用重新加。
- 运行启动函数。老版本通常是:
zmap_main新版本可能是:
zmap如果命令窗口没报错,并弹出一个带菜单栏的主界面,说明安装成功。有的版本会同时弹出一些日志窗口,不用管,那些是启动时的信息输出。
2.3 用自带示例数据做启动自检
环境是否真的可用,最靠谱的验证方式是跑一份示例数据。ZMAP压缩包自带的data目录下一般会有测试数据集,比如合成地震目录(synthetic catalog)或者某个区域的公开目录示例。
在ZMAP主界面上找到加载数据的菜单,通常叫Load Catalog,选中示例数据文件,加载后再看地图窗口能不能显示出地震点。如果能显示点,说明数据读入正常;后续再试试算b值、画图,一套流程走通,环境就算验证完毕。
这一步看起来很基础,但我建议每个人都做一次。因为ZMAP整合了太多依赖,有时候你以为装好了,实际某个子函数缺失,只有触发到那个功能才会报错。用示例数据提前把所有常用功能点一遍,后面处理自己数据的时候就踏实很多。
3. 把杂乱的地震目录变成ZMAP能吃的格式
3.1 ZMAP数据格式约定
ZMAP的数据格式本质上是一个纯文本矩阵,不需要像Excel那样做复杂的表头映射。它约定每一列代表一个字段,列与列之间用空格或制表符分隔。最常见的列顺序是:
- 第1列:经度(度)
- 第2列:纬度(度)
- 第3列:年份(整数)
- 第4列:月份(整数)
- 第5列:日(整数)
- 第6列:震级
- 第7列:深度(km)
- 后续列:水平误差、垂直误差、时间误差等,不同版本略有差异
也就是说,ZMAP最关心的“七要素”是经度、纬度、时间(年、月、日)、震级、深度。如果你的目录里还有发震时刻的时、分、秒,需要把这些信息存成另外的列,或者干脆在导入前把日期列合并转换。很多第三方地震目录下载下来是带时分秒的,转成ZMAP格式时只需要保留年月日作为时间标识。
有一点要特别说明:ZMAP读取数据时默认按固定列顺序解析。如果你的文件列顺序不对,程序不会报错,但画出来的地震点位置会完全错乱。看起来经纬度没问题,实际算b值时用的是错列的震级,结果自然全错。所以数据加载后,第一时间检查地图上地震点的坐标范围是否和你预期一致,以及震级统计是否合理。
3.2 七列必备字段与扩展列
如果你的数据只有经纬度和震级,能不能算b值?可以,但会少很多功能。深度列缺失的话,ZMAP的深度剖面图做不了,一些基于深度的分析模块也用不了;如果时间只精确到年,时间窗口扫描时分辨率就太粗了。
所以我在清洗数据时会尽量补全字段。比如中国地震台网目录里通常包含发震时刻、纬度、经度、深度、震级,这些字段足够转换成七列格式。国外一些目录还能额外提供震级类型、定位误差、台站数等信息,这些可以放到后续列里,ZMAP不会强制读取,但在某些模块里能用上。
如果原始数据里深度是0或负值,要特别警惕。有些目录对地表事件的深度定义为0,有些则用负值表示人工爆破或非天然事件。统一清洗时我会先查一下目录说明,再决定是剔除还是修正,不要直接拿来用。
3.3 从CSV到ZMAP的批量转换代码
这里给一段我自己常用的MATLAB转换代码,以CSV格式的地震目录为例。假设你的CSV文件有这些列:time, lat, lon, depth, mag,其中time是形如2020-01-01 12:34:56的文本。
% 读取CSV T = readtable('catalog.csv'); % 解析时间列 t = datetime(T.time, 'InputFormat', 'yyyy-MM-dd HH:mm:ss'); % 提取年月日 year = year(t); month = month(t); day = day(t); % 按ZMAP列顺序组合:经度 纬度 年 月 日 震级 深度 data = [T.lon, T.lat, year, month, day, T.mag, T.depth]; % 剔除NaN行 data = data(~any(isnan(data), 2), :); % 写为文本文件 writematrix(data, 'catalog_zmap.txt', 'Delimiter', ' ');用writematrix输出时,ZMAP能直接读这个空格分隔的文本文件。如果你的MATLAB版本比较老没有writematrix,用dlmwrite('catalog_zmap.txt', data, 'delimiter', ' ')也完全可以。
转换完成之后,再强调一遍:先在ZMAP里加载这个文件,检查地图上的点有没有落在目标区域。这一步能拦截绝大多数格式错误。
3.4 数据清洗:决定b值下限的那些隐藏问题
数据清洗看起来和b值计算无关,实际上影响非常大。b值是通过对震级分布做统计得到的,目录里的脏数据会直接污染统计结果。
我处理目录时通常会做这几步:
- 去重:同一个地震可能被多个台网重复记录,或者标准目录里出现重复行。判断依据一般是发震时刻和经纬度都相同或非常接近。这种重复事件会让样本量虚高,算出来的a值偏高。
- 剔除异常深度:深度小于0或者超过合理地壳厚度的记录,多半是定位错误。浅表人工事件和天然地震也不能混在一起。
- 统一震级类型:这是很多人忽略的点。同一个目录里,可能有的记录是面波震级Ms,有的是体波震级mb,有的是矩震级Mw。不同震级类型之间系统性相差零点几,如果混在一起用,震级频度曲线会在某个震级段出现不自然的台阶,b值也会被拉偏。
- 设置分析区域边界:ZMAP本身能框选区域,但建议在导入前就裁剪一次,避免把目录里与研究区无关的事件带入统计。
4. 计算b值:从单区域G-R曲线到空间扫描图
4.1 单区域b值计算操作流程
在ZMAP中算单区域b值,大概的操作路径是:
- 加载自定义数据目录;
- 用鼠标或手动设置经纬度范围框选研究区;
- 打开震级-频度分布(FMD)模块;
- 选择计算b值的按钮,通常标识为
Compute b-value或Estimate b-value。
ZMAP会先画出非累积频度曲线和累积频度曲线,然后自动估计最小完整性震级Mc,再用极大似然法拟合b值。结果的输出信息里会包含:
- b值;
- a值;
- 样本数N;
- Mc值;
- 标准误差。
正常情况下,b值应该在1附近。如果你算出一个0.3或者2.5,不要继续往下做,回头检查数据有没有问题。
极大似然估计b值的公式是:
b = log10(e) / (Mmean - Mc)
其中Mmean是震级大于等于Mc的事件的平均震级。这个公式来自Aki和Utsu,原理并不复杂,但它说明了一个关键点:b值由“震级偏离Mc的程度”决定。如果Mc取值不对,Mmean也会随之变化,b值自然就偏了。
4.2 Mc是b值的“地基”,先把它定了
Mc,即最小完整性震级,指该震级以上的地震能够被台网完整记录而不漏测。Mc以下的地震数量是不完整的,如果硬把这些数据纳入拟合,G-R曲线会在低震级端明显向下弯,拟合出来的b值会偏高。
ZMAP里常见的Mc估计方法包括:
- 最大曲率法(MAXC):把震级-频度曲线的曲率最大点对应的震级作为Mc。这个方法简单直观,但容易受个别异常震级bin的影响。
- 拟合优度法(GFT):假设不同Mc值下,观测频度和理论G-R模型拟合效果最好的那个Mc。这个方法更稳健,尤其在数据质量一般的时候。
实际操作中,我通常先用MAXC自动估算一个Mc,再手动调高或者调低0.1、0.2,观察b值变化幅度。如果b值随Mc的小幅调整变化很大,说明你的数据在Mc附近不够稳定,论文里需要特别说明。
举一个我实际遇到的例子:某目录样本量800多,MAXC给出的Mc=1.0,此时b值0.86;把Mc改成1.5之后,b值变成1.12;改成2.0之后,样本只剩下100多个,b值变成1.25。这种不稳定性说明目录低震级端记录不完整。而好的目录通常Mc变动0.1-0.2,b值基本稳定在误差范围内。所以在报告b值时,一定要写明Mc取值,否则读者无法判断你的结果是否可靠。
4.3 空间扫描:生成b值空间分布图
单区域b值只是第一步,很多研究的目标是一张“b值空间分布图”。ZMAP的空间扫描原理并不复杂:把研究区划分成规则网格,对每个网格节点,找到距离它最近的N个地震(比如50个或100个),然后用这N个地震计算b值,赋值给该节点,最后插值成连续分布图。
操作上需要注意三个参数:
- 网格间距:对大多数区域研究,0.1度到0.2度是比较常用的选择。网格太小会导致计算量剧增,且相邻节点样本重叠度过高,图面平滑但缺乏独立性。
- 每个节点的最小事件数:这个值决定b值估计的稳定性。最少50个,建议100个以上。样本太少,b值的标准误差会大到没有意义。
- 震级范围:扫描时要先确定统一的Mc值,不能每个节点单独取不同Mc,否则不同网格的b值可比性很差。
生成空间图之后,别忘了叠加研究区的主要断层或者构造边界,对比b值低值异常带与断层位置的关系。这张图通常就是论文的核心图件之一。
4.4 时间维度上的b值变化
除了空间分布,b值随时间的变化也是研究热点。做法是用滑动窗口把时间轴切段,对每个窗口内的地震目录计算b值,最后得到一条b值-时间曲线。
比如你研究一个2000年至今的目录,可以设置窗口长度为5年、滑动步长为1年,每个窗口独立计算b值。ZMAP里有一些交互式菜单支持这类分析,但如果你需要完全控制参数,建议自己写循环调用ZMAP的核心函数。
这里有一个容易被忽略的问题:时间窗口的样本量。如果窗口内地震数量少于60个,b值的置信区间会非常宽。窗口取短了,时间分辨率高但误差大;窗口取长了,误差小但分辨率低。实际工作中要根据目录的总样本量来回调整。我一般要求每个窗口至少包含80到100个地震,实在不足就放宽震级范围,比如把Mc从1.0提高到1.5,让窗口内的可用事件数量上去。
5. 别急着下结论:b值的不确定性、检验与常见误读
5.1 用误差和置信区间看b值
很多新手看到ZMAP输出一个b=0.85,就直接拿去跟文献里的b=1.05对比,然后得出结论“我们这里应力更高”。这是非常危险的操作。b值本身有不确定性,两个数值之间的差异可能完全在误差范围内。
ZMAP输出b值时一般会附带标准误差。极大似然估计的b值标准误差可以近似用:
sigma(b) = b / sqrt(N)
来估计。其中N是参与拟合的地震事件数。如果样本量只有50个,b=1.0,那么标准误差大约是0.14;如果样本量有200个,标准误差降到0.07左右。这意味着,小样本的b值对比几乎没有说服力。
我在论文里通常这样处理:先列出每个子区域的b值及其标准误差,再做两个区域b值是否显著差异的统计检验。ZMAP有部分检验功能,但更稳妥的办法是把b值和事件数导出,自己用标准公式或者MATLAB的统计函数做检验。
5.2 Bootstrap检验怎么用
Bootstrap是评估b值置信区间最实用的一种方法。它的思想是:把已有的地震目录当作总体,进行有放回的重采样,生成大量与原目录样本量相同的“伪目录”,对每个伪目录重新计算b值,最终得到b值的分布和置信区间。
ZMAP自带Bootstrap功能,操作上很简单:选择Bootstrap模块,设置重采样次数(一般200到500次足够稳定),程序会输出b值的均值、标准差和置信区间。如果置信区间上下界跨度很大,说明你的目录样本量不足以支撑精确的b值估计。
在研究报告或论文中,我强烈建议给每个b值都配上Bootstrap置信区间。审稿人对b值图最常见的质疑就是“怎么确定这个b值不是随机涨落出来的?”你只要给出置信区间,这个问题就能顺利化解。
5.3 论文写作里常见的三个误读
误读一:把非累积频度曲线的低震级端台阶当成b值变化。非累积频度曲线(即每个震级bin的地震频次)在低震级端通常会向下弯或出现抖动,这是因为漏测。如果把这种弯曲解释为“b值非线性”,就混淆了数据完整性和真实物理特征。处理方式是严格基于Mc以上的数据做线性拟合。
误读二:b值低就断言即将发生强震。这是最常见的泛化误读。b值低只能说明统计时段内大地震相对占比高,反映该区域可能处于高应力背景。它不等于发震概率,更不是临震预报指标。写结论时要非常谨慎,避免过度解读。
误读三:空间图上颜色对比不做显著性检验。不同网格节点的b值用的是不同数量的事件,样本量差异很大。一个节点有300个事件,b=0.8;另一个节点只有50个事件,b=0.9,两者可能在统计上毫无差异。出图前应该对“b值异常区”做显著性检验,只把置信度高的差异在图上突出显示。
6. 我实际使用ZMAP时踩过的坑(排错与杂项)
6.1 高频报错与对策
下面是我在不同机器上使用ZMAP时遇到过的几类常见问题,整理成表格供参考。
| 报错信息 | 可能原因 | 解决办法 |
|---|---|---|
Undefined function or variable 'zmap_main' | 路径未正确添加 | 在ZMAP主目录执行addpath(genpath(pwd)); savepath; |
plotyy was removed | 老版本ZMAP与新版MATLAB不兼容 | 换用新版ZMAP,或安装旧版MATLAB |
| 地图窗口空白,没有地震点 | 数据列顺序错乱或经纬度范围设置不对 | 检查数据文件各列含义,重新设置地图范围 |
Out of memory | 目录数据量过大或网格过密 | 先按区域裁剪数据,加大网格间距 |
| 加载数据时中文路径乱码 | 解压路径包含中文和空格 | 把整个ZMAP目录和你的数据目录放到英文路径下 |
6.2 大数据目录的处理技巧
如果你的地震目录有几十万条记录,直接加载进ZMAP会让交互界面变得非常卡顿。空间扫描时,每个节点都要从全目录里搜索最近N个事件,数据量一大,计算时间会急剧上升。
我的做法是分而治之:
- 先按研究区域裁剪目录,只保留目标经纬度范围内的地震;
- 再按时间段裁剪,除非你有明确的长期演化研究需求,否则不必把近50年的数据一次全部加载;
- 空间扫描时,先用较大的网格间距(比如0.2度)跑一遍看趋势,再对重点区域用0.1度加密。
ZMAP本身没有内置并行计算来加速所有环节,所以数据量大的时候,预处理比后续调整参数更有效率。
6.3 结果图的导出策略
ZMAP生成的图本质上是一个MATLAB figure。很多老教程推荐直接用print导出:
print('-dpng', '-r300', 'bvalue_map.png');新版MATLAB里更推荐使用:
exportgraphics(gcf, 'bvalue_map.png', 'Resolution', 300);区别在于exportgraphics能保持图中文字和字体比例不畸形,导出的图片更适合投稿。我一般会把图的大小、颜色条位置、色标范围统一设置好之后,再导出成PNG和PDF两个版本。PNG用来快速预览,PDF用于论文排版。
6.4 一个长期使用者的建议
最后聊一点不太属于教程、但我觉得很有价值的个人经验。
ZMAP虽然自带图形界面,但真正的效率提升来自于你把它当做一个可编程的计算平台。如果你只是每次点菜单、截图、记录结果,那么一个项目几十次分析下来,参数记录很快就会乱掉。我的习惯是:把ZMAP的核心函数封装成自己的脚本,统一传入目录文件、经纬度范围、Mc、网格参数,输出b值矩阵和统计指标。这样每次分析都有记录,实验可复现,改一个参数就能批量重跑所有结果。
把数据清洗脚本、ZMAP调用脚本、出图脚本分开存放,每次处理完数据都保存一份中间结果.mat文件。长期做地震目录分析,数据版本管理的重要性甚至比代码更重要。我吃过一次亏:跑了一个星期的扫描,结果发现原始目录在第三天后被另一个脚本覆盖了一部分,后面所有结果全部作废。从那以后,原始数据永远只读,任何清洗操作都另存为新文件。
说实话,ZMAP不是什么精致漂亮的现代软件,它带着浓厚的学术工具风格,菜单有些乱,界面也谈不上美观。但它的统计方法和分析框架经受了大量文献的检验,你在论文里写“using ZMAP”时,审稿人是认可的。对做地震活动性研究的人来说,把这套工具用熟,能省下大量时间,把精力放在真正需要动脑的数据解释和模型验证上。
本文还有配套的精品资源,点击获取