Part I: Python Fundamentals

Chapter 3

Data Structures for Petroleum Data

schedule25 min readfitness_center10 exercises
auto_awesomeAI Key Takeaways

Get the TL;DR and the key concepts before you dive in — or as a quick review after.

Petroleum engineering generates enormous volumes of data across every phase of a well's life. Drilling reports, well logs, production records, fluid analyses, pressure tests, seismic surveys: each arrives in a different format, at a different frequency, with a different structure. An engineer who cannot organize, access, and manipulate this data efficiently loses most of the working day to fighting spreadsheets instead of solving problems.

Python provides four core data structures (lists, tuples, dictionaries, and sets), each suited to a different kind of petroleum data. This chapter teaches all four through real engineering use cases, then moves to the file formats you will encounter in every petroleum engineering workflow: CSV, JSON, and LAS.

infoWhat You Will Learn

  • Lists: ordered, changeable collections for production histories, depth arrays, and time series
  • Tuples: fixed collections for coordinates, well locations, and parameter sets that must not change
  • Dictionaries: key-value lookups for well headers, fluid properties, and configuration data
  • Sets: unique collections for well inventories, field comparisons, and deduplication
  • File I/O: reading and writing CSV, JSON, and LAS files, the three formats that dominate petroleum data exchange
  • List and dictionary comprehensions: concise, readable data transformations

Lists: Production Histories and Depth Arrays

A list is an ordered, changeable sequence of values. In petroleum engineering, lists naturally represent anything that comes in a sequence: monthly production rates, depth intervals, casing diameters, or a series of pressure readings.

main.py

Indexing and Slicing

Lists are zero-indexed; the first element is at position 0. This is a universal convention in programming, and while it takes some adjustment if you are coming from Excel (where row 1 is the first row), it becomes natural quickly.

main.py

Modifying Lists: Adding and Updating Data

Production data grows over time. New months arrive, wells come online, records get corrected. Lists are mutable; you can add, remove, and change elements.

main.py

Water Cut Trend: Lists + Visualization

Water cut, the fraction of produced liquid that is water, is one of the most important surveillance metrics in production engineering. A rising water cut means the reservoir's water is encroaching on the oil zone. Here is how to compute and visualize it from two lists.

main.py
Oil rate, water rate, and water cut over the first 13 months of Well OD-003. The water cut rises steadily from 12% to 56%, indicating progressive water encroachment from the aquifer. This trend, if it continues, will make the well uneconomic within 2–3 years without intervention.
Oil rate, water rate, and water cut over the first 13 months of Well OD-003. The water cut rises steadily from 12% to 56%, indicating progressive water encroachment from the aquifer. This trend, if it continues, will make the well uneconomic within 2–3 years without intervention.

This is a standard production surveillance plot, the kind that appears in every monthly production report. The crossing of the oil and water curves is a significant event that reservoir engineers watch closely. In this well, it happens between months 10 and 11: the water rate overtakes the oil rate at month 11, when water cut first passes 50%.

Tuples: Fixed Data That Must Not Change

A tuple is like a list, but it cannot be modified after creation. That sounds like a limitation, but it is actually a safety feature. Some data should never change: well coordinates, datum elevations, fundamental constants, and calibration values.

main.py
main.py

If you need a collection that cannot be accidentally modified during processing (coordinates, physical constants, calibration data) use a tuple. If you need a collection that grows, shrinks, or gets updated (production data, well lists, calculation results) use a list.

infoWhy immutability is a *safety* feature, not a limitation

Here is the failure this prevents. In a long analysis pipeline (data loaded, well coordinates read, dozens of functions transforming it) one function mutates well_location[0] by accident, meaning to write to a different list. With a list, the bug is silent: every downstream calculation now uses corrupted coordinates, and the wrong wells get drilled until someone notices. With a tuple, that line raises TypeError on the spot and the pipeline halts. Tuples turn a class of silent data-corruption bugs into loud crash bugs, and in engineering you want loud bugs.

Dictionaries: Well Headers and Property Lookups

A dictionary stores key-value pairs. In petroleum engineering, this maps directly to the concept of a well header: a structured record where each field has a name and a value.

main.py

Nested Dictionaries: A Field Database

A real field contains multiple wells, each with its own header. A dictionary of dictionaries is a natural way to represent this.

main.py
main.py
Current oil production by well for OML 58. Well OD-007 (recently completed) is the single largest producer. OD-002 is shut in for workover. The bar heights show that the field's output depends heavily on two wells (OD-007 and OD-005 together are ~64% of field rate) - a concentration risk that should be flagged in any production review.
Current oil production by well for OML 58. Well OD-007 (recently completed) is the single largest producer. OD-002 is shut in for workover. The bar heights show that the field's output depends heavily on two wells (OD-007 and OD-005 together are ~64% of field rate) - a concentration risk that should be flagged in any production review.

Sets: Unique Identifiers and Field Comparisons

Two databases should list the same wells, but do they? Which wells were drilled but never made it into the production system? Which formations show up across more than one field? Questions like these are about membership and uniqueness, and the tool built for them is the set: an unordered collection of unique values.

main.py

OD-004 and OD-006 were drilled but are not in the production database; they might be abandoned, waiting on completion, or simply missing from the records. OD-008 is producing but has no drilling record, a data quality issue that needs investigation. These are exactly the kinds of discrepancies that a petroleum data engineer encounters daily, and sets make them trivial to find.

File I/O: The Three Formats of Petroleum Data

Everything so far has lived in memory; the moment Python exits, it is gone. Real petroleum data arrives in files and has to be written back to them, and three formats cover almost everything you will touch. Each matches a different shape of data (flat tables, nested records, depth-indexed logs), and choosing the wrong one turns a one-line read into an afternoon of parsing.

CSV: Production Data

Production data almost always reaches you as CSV, comma-separated values, the format SCADA systems, databases, and regulatory agencies all export to. Reading and writing it cleanly is step one of nearly every analysis in this book.

main.py
main.py

JSON: Well Configuration and Metadata

When data has structure (a well with nested completion intervals, a configuration with options inside options) CSV's flat grid stops working. JSON (JavaScript Object Notation) handles the nesting, and it maps directly to Python dictionaries, which is why API responses and metadata almost always arrive in it.

main.py

LAS: Well Log Data

LAS (Log ASCII Standard) is the petroleum industry's standard format for well log data. Every well log you will ever work with (gamma ray, resistivity, density, neutron, sonic) is stored or exchanged as a LAS file. The code below creates one from scratch so you can see its structure: header sections (~VERSION, ~WELL, ~CURVE) followed by columnar ASCII data.

infoHand-rolled vs. lasio

We parse LAS by hand here because the format is small enough to understand from first principles, and because reading a few hundred lines of standard ASCII is the fastest way to learn what a LAS file actually contains. In real workflows, every petrophysicist uses the lasio library: las = lasio.read("od003_logs.las") gives you a typed object with curves, units, and metadata in one line. We switch to lasio from chapter 6 onward.

main.py
main.py
Triple-combo log for OD-003 (9,000–9,500 ft). Low GR, high resistivity, and density-neutron separation at 9,150–9,350 ft indicate a hydrocarbon-bearing pay zone.
Triple-combo log for OD-003 (9,000–9,500 ft). Low GR, high resistivity, and density-neutron separation at 9,150–9,350 ft indicate a hydrocarbon-bearing pay zone.

This is a triple-combo log, the everyday display a petrophysicist works from. The three tracks together tell you whether a rock interval holds hydrocarbons, how porous it is, and how thick the pay zone is. Watch the density and neutron curves over the pay zone: the neutron porosity dips below the porosity the density implies, and that crossover is the classic signature of light hydrocarbons in the rock. We compute quantitative answers from these curves in Chapter 7 (Well Log Analysis).

List and Dictionary Comprehensions

You will constantly need to build one collection from another: a water cut from each month's rates, the names of every well above a rate threshold, a lookup table keyed by well. Written as a loop, that is four lines of boilerplate; written as a comprehension, it is a single readable expression.

main.py

Comprehensions are not just shorter; they are more readable when the transformation is simple. When the logic inside gets complex (multiple conditions, nested calculations), a regular loop with a named function is the clearer choice.

infoA modern shortcut: merging dictionaries with `|`

Since Python 3.9 you can merge two dictionaries with the | operator, handy when you apply an update to a well record. If field["OD-003"] is the current record and a new report changes only a couple of fields, field["OD-003"] | {"oil_rate_bopd": 1042, "water_cut_pct": 58} returns a new dict with those fields patched in (the right-hand side wins on shared keys), leaving the original untouched. It is the clean, non-mutating way to apply a patch, far tidier than copying the dict and reassigning keys one by one.

info`dict.get()`: looking up a key that may not exist

Real petroleum data is often missing fields. well_header["api_number"] raises KeyError if the key is absent. well_header.get("api_number") returns None instead. well_header.get("api_number", "unknown") returns your fallback value. Use .get() whenever you're not certain the key is present; it's the standard Python idiom for "look up if available, fall back if not."

Summary

This chapter covered the data structures that underpin all petroleum data processing:

  • Lists store ordered sequences: production histories, depth arrays, casing programs. They are mutable and indexed from zero.
  • Tuples store fixed data: well coordinates, casing specifications, reference constants. Their immutability is a safety feature, not a limitation.
  • Dictionaries store key-value pairs: well headers, completion records, field databases. Nested dictionaries model the hierarchical structure of petroleum data naturally.
  • Sets store unique values: well inventories, formation lists, data reconciliation. Set operations (intersection, difference, union) answer data quality questions instantly.
  • CSV files hold tabular production and engineering data. Python's csv module reads and writes them with minimal code.
  • JSON files hold structured metadata and configuration. They map directly to Python dictionaries.
  • LAS files hold well log data, the petroleum industry's standard since the CWLS first published the Log ASCII Standard in 1990. The columnar format contains depth-indexed measurements from wireline and LWD tools.
  • Comprehensions provide concise, readable data transformations for lists and dictionaries.

In the next chapter, we move from Python's built-in structures to the libraries that make petroleum data science practical: NumPy for numerical computation and Pandas for tabular data analysis at scale.

Exercises

fitness_center
Exercise 3.1Practice

: Casing String Inventory

A well's casing program consists of the following strings: CasingOD (in)Weight (lb/ft)GradeSetting Depth (ft)Conductor36192H-40500Surface2094K-552,800...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 3.2Practice

: Well Header Builder

Write a function build_well_header() that accepts keyword arguments for well name, field, operator, total depth, TVD, well type, spud date, and status...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 3.3Practice

: Production Data Reconciliation

You receive two CSV files from different departments. One contains drilling data for wells: OD-001 through OD-010. The other contains production data ...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 3.4Practice

: JSON Well Completion Report

Create a JSON file representing a multi-zone completion with at least three perforation intervals. Each interval should include top depth, bottom dept...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 3.5Practice

: LAS File Explorer

Write a function las_summary(filepath) that reads a LAS file and prints: (a) the well name and field from the header, (b) the depth range and step siz...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 3.6Practice

: Water Cut Alarm System

Using the production data from this chapter, write a program that monitors water cut trends. For each well, calculate the water cut for each month and...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 3.7Practice

: Drilling Fluid Inventory

A drilling operation maintains an inventory of different mud types and additives stored as a dictionary of dictionaries: ``python inventory = { Barite...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 3.8Practice

: Multi-Well Production Comparison

Create a dictionary containing 12 months of production data for four wells. For each well, store monthly oil rate, water rate, and gas rate. Write a p...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 3.9Practice

: Formation Tops Database

Geologists maintain a database of formation tops, the depth at which each geological formation is encountered in each well. Create a dictionary struct...

arrow_forward
codePythonSolve Nowarrow_forward
fitness_center
Exercise 3.10Practice

: Data Format Converter

Write a program that reads the field_production.csv file created in this chapter and converts it to: (a) a JSON file grouped by well (each well has an...

arrow_forward
codePythonSolve Nowarrow_forward