简介:geolog-app 是一个基于 Python 开发的地质数据处理与可视化应用,面向地质科研人员、数据分析学习者以及希望掌握 GIS 和 Web 开发技能的编程爱好者。压缩包仅有 14KB,共含 22 个文件,以 14 个 Python 脚本为核心,覆盖 Django 后端的配置、URL 路由、视图、模型、迁移等模块,同时提供 SQLite 数据库、HTML/CSS 前端页面、依赖清单、部署配置和 README,结构紧凑,便于快速定位与研读。目前已有 162 人学习下载。通过该项目,读者可以完整了解一个微型地质 Web 应用的代码组织方式,看到 Python 在地质数据清洗、空间分析与图表展示中的实际落地,体会到从后端逻辑到前端渲染的串联方法,也为后续引入 NumPy、Pandas、GeoPandas 等更复杂的数据科学和 GIS 工具建立了清晰的参考模板。对于刚接触 Django 的开发者,这份代码还能展示 MTV 架构的常见写法,帮助理解 URL 分发、模型迁移与模板渲染之间的协作关系。
1. geolog-app 是干什么的:写给数据一堆但出不来图的人
听到 geolog-app 这个名字,先别急着把它当成又一个商业地质软件。它更像一个围绕“深度—数值—分层标记”三条数据线做整理的轻量工具,解决的是测井数据和地质分层之间的衔接问题。常见场景是:你手里有一口井的 LAS 或 CSV,曲线在,深度在,但要把层位顶底深度标出来、把两口井放在同一深度轴上对比,却总在格式转换和深度对不齐上耗掉大半天。这类活儿是 geolog-app 的主场,它不替你做地质解释,只把深度校正、重采样、分层标记和图件导出这条管线整理到一个可重复执行的工作流里。
适合谁用?一种是每天在商业工作站里点鼠标、但想省掉重复整理步骤的从业者;另一种是拿到别人给的数据、第一件事是确认深度和曲线是否干净的工程师。如果你正处于“数据越多越不敢动”的状态,这个方向值得投入。
2. 先让 geolog-app 跑起来:依赖安装与最小启动命令
2.1 先理解三张表:深度、数值、分层标记
用 geolog-app 之前,建议把数据结构先拆成三类。第一类是深度轴,也就是每一条曲线对应的采样深度点;第二类是曲线数值,比如 GR、SP、电阻率这些测井响应;第三类是分层标记,也就是你要人工或半自动确定的层位顶底深度。这三者分开存、分开处理,后续所有操作才会不打架。
我最开始用的时候,习惯把所有东西塞进一个 DataFrame,结果深度校正后索引全乱了,分层标记也跟着错位。后来改成三表分离:深度轴单独一个数组,数值曲线按名字存在字典里,分层标记单独用一个 CSV 维护。这样做的直接好处是,无论哪一步做错了,只要重新执行对应步骤就行,是一个可复跑的工作流,而不是一次性的脚本。
2.2 安装与环境准备
geolog-app 的依赖不算复杂,核心是 lasio、pandas、numpy、matplotlib 这几个库。lasio 负责解析 LAS 格式,pandas 负责表格操作,matplotlib 负责出图。以下是我常用的环境初始化方式:
python -m venv venv source venv/bin/activate pip install lasio pandas numpy matplotlib scipy逻辑上先建虚拟环境,避免污染系统 Python;lasio 是测井行业通用的 LAS 文件解析库,只要不是太老的文件版本都能读。scipy 是可选的,我在做曲线平滑时才用得到,如果你只做分层标记,可以先不装。安装完成后,建议先空跑一遍导入检查,而不是直接去读数据文件,这样可以区分“环境问题”和“数据问题”。
2.3 最小启动命令:读入一口井并确认深度轴
环境准备好了,第一步是把一口井的 LAS 文件读进来,然后打印最基本的信息:曲线名、单位、起始深度和终止深度。这一步的意义在于,很多问题在还没开始处理之前就会暴露出来,比如单位全乱、深度轴顺序颠倒等。
import lasio las = lasio.read("demo.las") for curve in las.curves: print(curve.mnemonic, curve.unit) depth = las.index print("起始深度:", depth.min(), "结束深度:", depth.max()) print("采样点数:", len(depth))lasio.read 默认返回一个 LAS 对象,las.index 是深度轴,las["GR"] 可以按曲线名取数值。第一次跑通的时候,记住先看曲线单位,再看深度范围。单位不一致的情况不在少数,比如 GR 是 API、电阻率是 OHMM,这些在后续绘图时如果不换算,图例就没法统一。
3. 深度轴校正与重采样:两个必调参数看清数据差异
3.1 深度漂移校正:先对齐再做别的
测井深度漂移是个老话题。同一口井不同趟次测井,深度轴可能差出几米;不同井之间,补心高、井深基准也可能不同。在校正之前做任何分层都等于在沙地上盖楼。常见的做法是选一条参考曲线,比如用电缆深度校正后的 GR 作为基准,把需要校正的曲线整体平移或分段平移。
import numpy as np depth = las.index gr_raw = las["GR"] # 校正量由人工对比参考GR确定,单位是米 shift = 2.5 depth_corr = depth - shift # 用校正后的深度重新插值,保证深度轴单调 gr_corrected = np.interp(depth, depth_corr, gr_raw)这里 shift 的含义很关键:它是“需要减去的偏移量”,正值表示当前深度比参考深度偏大。某个方向上如果校正后出现了重复深度或倒序深度,说明方向取反了。这个参数几乎没有自动化的万能解法,因为漂移原因多样,我在实际项目中通常靠直观对比曲线峰谷来定初始值,再微调 0.25~0.5 米的量级。
3.2 平滑窗口:用中值滤波还是均值滤波
曲线毛刺太多时,直接自动分层会抓出一堆假突变。中值滤波比均值滤波更稳,因为它保留台阶边缘,不会把真实层界面磨平。这个特性对层位识别非常重要,因为你要的是突变的台阶,而均值滤波会把台阶过渡拉长,让顶底边界看着像渐变。
from scipy.ndimage import median_filter # size 是滑动窗口的点数,按采样间隔换算 # 例如采样间隔0.125米,size=5 相当于平滑0.625米 gr_smooth = median_filter(gr_corrected, size=5)size 怎么选?采样间隔越密,理论上窗口可以开得越大,但窗口过大会削平薄层的响应极值。对 0.125 米采样的 GR 曲线,我用 5 到 9 个点起步,然后对比原曲线看形态是否失真。对层厚小于 1 米的薄层,窗口建议控制在 5 个点以内。这个参数属于“看数据说话”的类型,没有通吃的值。
3.3 重采样:统一井间的深度网格
井间对比时,每口井的采样间隔和起始深度都不同,比如 A 井从 2000 米开始采、间隔 0.125 米,而 B 井从 1999.8 米开始、间隔 0.5 米。如果不统一网格,后面所有井间对比图都错位。重采样的做法是定义一个统一的深度序列,然后把每口井的曲线插值到这套序列上来。
# 定义统一网格:从 2000 米到 2500 米,间隔 0.125 米 new_depth = np.arange(2000.0, 2500.0, 0.125) # 线性插值到新网格 gr_resampled = np.interp(new_depth, depth_corr, gr_smooth, left=np.nan, right=np.nan)参数说明:left 和 right 设置为 nan,是为了让超出原井深度范围的区域不参与后续统计,否则外推数据会形成虚假的水平线。此外,超过原深度范围而插值得到的 nan 会在绘图时形成空白,这是期望行为。如果用的是更高阶插值,比如样条,请留意过冲震荡,薄层处尤其容易出负值,这在 GR 曲线上就是明显的假响应。
4. 分层作业:手动标记与自动候选的正确打开方式
4.1 手动分层模式:用表格维护顶底深度
自动分层在复杂地质条件下并不可靠,至少在目前,最终解释权还是应该交给人工。手动分层不代表用鼠标在软件里拉线,而是通过观察平滑后的曲线,把层位顶底深度记录到一个结构化文件里。这样既保留了人的判断,又让结果可以被脚本复用。
# markers.csv: layer_top,layer_bottom,layer_name # 2385.0,2420.5,"Layer_A" # 2420.5,2471.0,"Layer_B" import pandas as pd markers = pd.read_csv("markers.csv") markers["thickness"] = markers["layer_bottom"] - markers["layer_top"] print(markers)CSV 是分层标记最好的载体,因为顶底深度本身就是两列数字。每次手工修正分层时,只需要改 CSV 里对应行的数字再保存,处理脚本不需要改。这种做法对我来说最大的价值是历史可追溯:每一版分层文件都保留了谁在什么时候调整过哪个层位,而不是一个不可回放的图形状态。
4.2 自动分层候选:一阶差分找突变点
自动分层可以做,但定位应是“候选生成器”,而不是“最终解释器”。对 GR 这类能反映岩性变化的曲线,地层界面往往对应曲线值的一阶差分极值。实现思路很简单:计算平滑后曲线的差分绝对值,设定一个阈值,超过阈值的位置就是候选界面。
diff = np.abs(np.diff(gr_smooth)) # 用98分位数做阈值,只挑最显著的突变 threshold = np.percentile(diff, 98) candidates = np.where(diff > threshold)[0] # 把索引换算成深度 candidate_depths = depth_corr[candidates] print("候选界面深度:", candidate_depths[:10])threshold 是这里唯一的关键参数。98 分位数通常是安全起点,如果候选太多,说明分层信号太碎,一是提高分位数到 99,二是先加大平滑窗口。如果候选太少或一个大突变掩盖了次级界面,可以改成在一个滑动区间内做局部百分位筛选。注意:自动候选必须和人工标记配合,我的经验是百分之八十的候选点可直接采用,剩下百分之二十因为薄层、断点或泥质条带需要手动剔除。不要试图把这个比例反过来。
5. geolog-app 避坑:5 条实测记录
5.1 深度轴相关的三个坑
现象一:一口井的 GR 曲线与邻井形态相似,但每套层位深度都系统性偏浅 3 米左右,人工分层时怎么都对齐不了。
原因:LAS 文件里的深度单位是英尺,而参考数据用的是米,差值正好来自单位换算。这类问题常见于数据源混杂的项目,录入时没有做单位统一。
解决:读取 LAS 后第一步打印 las.well 里的深度单位,确认是 feet 还是 meter,再统一换算。换算系数记住一个:1 英尺等于 0.3048 米。查出来的问题往往不是曲线问题而是单位问题。
现象二:在 geolog-app 里把曲线和数据表导出后,某口井的分层边界与原始测井图相差约半个采样间隔,怎么都对不上。
原因:深度校正和重采样之后,分层标记的深度基准没有同步更新。用旧深度轴上的分层数据去匹配新深度轴上的曲线,自然差出间隔。
解决:把“深度轴”作为唯一基准。重采样后立刻用新深度轴重新换算所有分层顶底深度,再做下一层操作。我在工作流里强制规定:所有分层修正只允许基于最新导出的一版校正深度,不能来回混用。
现象三:同一工区数口井分别来自不同批次测井,有的从补心高起算,有的从地面起算,井间对比时同一层位深度跳来跳去。
原因:深度基准不一致。这是数据管理问题,不是曲线问题。
解决:导入阶段明确记录每口井的深度基准类型,统一换算到同一基准后再进入后续流程。在分层文件里增加一列 depth_datum 做标记,虽然看起来多此一举,但若干口井对比时能省大量排查时间。
5.2 曲线数值与格式相关的两个坑
现象四:平滑处理后的 GR 曲线上出现几个异常深谷,形态与原始曲线完全不吻合,看起来像“挖坑”。
原因:LAS 文件里的缺失值记录为 -999.25,没有做缺失值屏蔽,直接送入中值滤波后这些异常值就会像真实低值一样参与运算并扩散到相邻点。
解决:所有处理前先屏蔽缺失值,用 np.where 将异常值替换为 nan,再在前处理里统一填充。中值滤波前把 nan 做插值处理,避免窗口内的异常值影响滤波输出。这一步是花了比较长的时间才真正重视起来的,属于典型的细节翻车。
现象五:把 CSV 导出的数据重新导入 geolog-app 后,深度列被当成字符串,排序结果完全错乱,分层文件顺序全变。
原因:CSV 导入时 pandas 自动推断类型失败,深度列含空值或单位字符,被识别为 object 类型。
解决:导入时显式指定 dtype:
depth_data = pd.read_csv("depth.csv", dtype={"depth": "float64"}) depth_sorted = depth_data.sort_values("depth").reset_index(drop=True)强制指定 float64 后,排序按数值而不是按字典序进行。这个坑几乎是必踩的,因为手工生成的 CSV 容易混入空格和单位后缀。养成导入时显式规定类型、然后立刻检查 dtype 的习惯,比事后调错要高效得多。
6. 导出剖面图与分层数据表:交付前最后一步
6.1 多井横向对比图的画法
geolog-app 的落点常在于“把结果给人看”。多井对比图的常见做法是每口井一列,深度轴间隔一致、自上而下排列。绘图时的关键点是统一深度范围,并且纵轴要翻转,让深度向下增大。
import matplotlib.pyplot as plt fig, axes = plt.subplots(1, n_wells, figsize=(3 * n_wells, 8)) for ax, name in zip(axes, well_names): ax.plot(wells[name]["GR"], wells[name]["depth"], linewidth=0.6) ax.invert_yaxis() ax.set_ylim(2000, 2500) # 强制所有井同一深度窗 ax.set_xlabel(name)这里 ylim 必须手动指定,让图幅范围一致,否则深度窗口会在各井之间跳动,对比无从谈起。invert_yaxis 把深度改成向下增大,符合地质图的阅读习惯。剖面图本身不是成果,但它是发现问题的工具,一半的深度错位问题靠这张图看得出来。
6.2 分层数据表的导出
分层成果最终要交出去,常见格式是分层数据表加剖面对比图。导出的分层表会包含层名、顶深、底深、视厚度,以及分层说明。一个实用细节是同时输出校正前后的深度对照,这样收数据的一方可以独立核验。
result = markers.copy() result["top_corr"] = result["layer_top"] + depth_shift_total result["bottom_corr"] = result["layer_bottom"] + depth_shift_total result.to_csv("layers_result.csv", index=False)每个数据交付清单一律附上校正参数,这是很重要的习惯,可避免对方拿到数据后发现深度对不上但找不到原因。这个检查习惯帮我省了很多次后续沟通的成本,也算是一个经验之谈:分层的准确性不只是靠曲线识别,更靠整条数据链路的每个中间台账都可追溯。如果你现在还在用“手动记录深度+手工描图”的方式做分层,强烈建议把 geolog-app 里这套深度校正与标记流程接起来跑一遍,经历过一次多井对比导出,就会感受到前后差异。希望帮到你。
本文还有配套的精品资源,点击获取