Exerciseschevron_rightChapter 6chevron_right6.1
fitness_center

Exercise 6.1

LAS File Inspection

Level 1
Chapter 6: Petroleum Data Sources
descriptionProblem

Before you analyse a well log, you inspect it. The header tells you the well, the field, the depth range, and which curves you actually have, all before you trust a single number. The starter holds a real-shape LAS file for OD-007 (a shale–sand–shale sequence) as LAS_TEXT. Note one resistivity reading is the LAS null value -999.25, and lasio turns that into NaN when it builds the DataFrame, which is exactly what you want.

Write three functions (read the file with lasio.read(io.StringIO(LAS_TEXT))):

  1. header_info(las): a dict with well, field, start_ft, stop_ft,

and step_ft pulled from the well section (las.well.WELL.value, las.well.FLD.value, STRT, STOP, STEP).

  1. curve_mnemonics(las): the list of curve mnemonics

([c.mnemonic for c in las.curves]), including the depth curve.

  1. curve_stats(las): from las.df(), return

{mnemonic: {"min": ..., "max": ..., "mean": ...}} for each data curve. Because lasio already mapped -999.25NaN, the stats skip the null automatically (that's why you never compute statistics on raw LAS data without null handling first).

The point: the header and a stats pass are the 30-second sanity check every petrophysicist runs before doing any real work on a log.

lightbulbHints (0/3)

Stuck? Reveal hints one at a time — they progress from nudge to near-solution.

codeYour solution
main.py
visibilityReveal reference solutionexpand_more

Try solving it yourself first — the hints walk you through it. The solution below is one valid approach; yours may differ and still be correct.

import lasio
import io
import pandas as pd  # las.df() builds a pandas DataFrame under the hood

LAS_TEXT = """~VERSION INFORMATION
 VERS.   2.0 : CWLS LOG ASCII STANDARD - VERSION 2.0
 WRAP.   NO  : ONE LINE PER DEPTH STEP
~WELL INFORMATION
 WELL.  OD-007 : Well Name
 FLD.   OML 58 : Field Name
 COMP.  NATIONAL PETRO : Company
 SRVC.  SCHLUMBERGER : Service Company
 DATE.  10-FEB-2025 : Log Date
 STRT.  9000.000 : START DEPTH (FT)
 STOP.  9005.500 : STOP DEPTH (FT)
 STEP.  0.500 : STEP (FT)
 NULL.  -999.2500 : NULL VALUE
~CURVE INFORMATION
 DEPT.FT   : Depth
 GR  .GAPI : Gamma Ray
 RT  .OHMM : Deep Resistivity
 RHOB.G/CC : Bulk Density
 NPHI.V/V  : Neutron Porosity
~A  DEPT       GR       RT      RHOB    NPHI
 9000.000   92.0     2.1     2.49    0.31
 9000.500   95.5     1.9     2.51    0.33
 9001.000   88.0     2.4     2.47    0.29
 9001.500   44.0    18.5     2.32    0.16
 9002.000   39.5    25.0     2.29    0.14
 9002.500   41.0  -999.25    2.30    0.15
 9003.000   38.0    31.0     2.28    0.13
 9003.500   47.0    15.0     2.34    0.17
 9004.000   90.0     2.2     2.50    0.32
 9004.500   96.0     1.8     2.52    0.34
 9005.000   93.0     2.0     2.49    0.31
 9005.500   91.0     2.3     2.48    0.30
"""


def header_info(las):
    return {
        "well": las.well.WELL.value,
        "field": las.well.FLD.value,
        "start_ft": float(las.well.STRT.value),
        "stop_ft": float(las.well.STOP.value),
        "step_ft": float(las.well.STEP.value),
    }


def curve_mnemonics(las):
    return [c.mnemonic for c in las.curves]


def curve_stats(las):
    df = las.df()
    return {
        col: {"min": float(df[col].min()), "max": float(df[col].max()), "mean": float(df[col].mean())}
        for col in df.columns
    }


las = lasio.read(io.StringIO(LAS_TEXT))
info = header_info(las)
print(f"{info['well']} ({info['field']}): {info['start_ft']:.0f}-{info['stop_ft']:.0f} ft, step {info['step_ft']} ft")
print("Curves:", curve_mnemonics(las))
for mnem, s in curve_stats(las).items():
    print(f"  {mnem:5s} min={s['min']:.2f} max={s['max']:.2f} mean={s['mean']:.2f}")

lockCopying code is a Full Access feature.