Read an EDI Survey#

This tutorial is the first practical step in most pyCSAMT workflows. It shows how to read one EDI file, a line directory, or a recursive survey tree, then turn the result into a station inventory that can be inspected, saved, passed to quality control, or used by downstream processing.

The goal is simple:

load the survey once, verify what was found, and keep a clean object for the next processing step.

What You Will Learn#

After this tutorial you should be able to:

  • read EDI files through the public pycsamt.api.read_edis() API

  • understand the returned APISurvey object

  • inspect station names, paths, frequency counts, and section availability

  • convert the survey summary to a pandas dataframe

  • handle recoverable parser errors during first inspection

  • choose a duplicate-station policy

  • run equivalent command-line inspection commands

  • decide whether the survey is ready for QC, static-shift correction, or inversion preparation

Input Layouts#

read_edis accepts a single file, a directory, a glob-like path, or a sequence of sources. The examples below use the bundled WILLY L18PLT line:

data/AMT/WILLY_DATA/L18PLT

Replace that path with your own EDI directory when you repeat the workflow. A common field layout is one folder per line:

data/AMT/WILLY_DATA/
    L18PLT/
      18-001A.edi
      18-002U.edi
      18-003A.edi
    L22PLT/
      22-001A.edi
      22-002U.edi

If you pass data/AMT/WILLY_DATA with recursive=True, pyCSAMT scans the line folders and reads every .edi file it can discover.

For a flat directory, the layout can be simpler:

data/
  edis/
    S001.edi
    S002.edi
    S003.edi

The same API call works for both layouts.

Read a Survey Directory#

Use pycsamt.api.read_edis() for the public v2 API. During first inspection, strict=False is usually the best choice because it keeps reading the remaining stations when one file has a recoverable issue.

 1from pycsamt.api import read_edis
 2
 3survey = read_edis(
 4    "data/AMT/WILLY_DATA/L18PLT",
 5    recursive=False,
 6    strict=False,
 7    on_dup="replace",
 8    progress=False,
 9)
10
11print(survey)

For the bundled line, the printed survey object is:

APISurvey: edi_survey
sites: 28
stations: 23-18-001A, 23-18-002U, 23-18-003A, 23-18-004A, 23-18-005U, 23-18-006A, 23-18-007U, 23-18-008U, ...
source: data/AMT/WILLY_DATA/L18PLT

The returned object is an APISurvey. It is a friendly public facade over the lower-level EDI collection. It keeps the high-level workflow readable while still exposing the raw collection when you need advanced operations.

1print(survey.n_sites)
2print(survey.stations[:5])
3print(survey.paths[:5])
4
5collection = survey.collection
6same_collection = survey.to_collection()

Example output:

28
['23-18-001A', '23-18-002U', '23-18-003A', '23-18-004A', '23-18-005U']
['18-001A.edi', '18-002U.edi', '18-003A.edi', '18-004A.edi', '18-005U.edi']

survey.collection is the object used by many lower-level EDI, site, editing, and QC helpers.

Read One EDI File#

For a single EDI file, use pycsamt.api.read_edi() when you want the raw EDIFile object directly:

1from pycsamt.api import read_edi
2
3edi = read_edi("data/AMT/WILLY_DATA/L18PLT/18-001A.edi")
4print(edi.station)

If you want the same APISurvey interface used by the rest of this tutorial, read the file with read_edis instead:

1from pycsamt.api import read_edis
2
3survey = read_edis(
4    "data/AMT/WILLY_DATA/L18PLT/18-001A.edi",
5    recursive=False,
6    progress=False,
7)
8print(survey.n_sites)
9print(survey.summary())

Example output:

23-18-001A
1
APIFrame: edi_survey_summary
kind: edi.summary
shape: 1 rows x 6 columns
columns: station, path, n_freq, tipper, spectra, ts

This is useful when you are writing reusable code that should accept either one file or many files.

Build a Station Inventory#

The fastest survey inventory is survey.summary(). It returns an APIFrame with a compact station-level table.

1summary = survey.summary()
2print(summary)
3
4inventory = summary.to_pandas(copy=True)
5print(inventory.head())

Example output:

   station        path  n_freq  tipper  spectra     ts
23-18-001A 18-001A.edi      53   False    False  False
23-18-002U 18-002U.edi      53   False    False  False
23-18-003A 18-003A.edi      53   False    False  False
23-18-004A 18-004A.edi      53   False    False  False
23-18-005U 18-005U.edi      53   False    False  False

The figures in this tutorial are generated by docs/scripts/generate_tutorial_read_edi.py. This overview shows that every loaded station has 53 impedance-frequency rows and that the bundled line does not include tipper, spectra, or time-series sections:

Station inventory and optional EDI section availability for L18PLT.

The default columns are:

station

Station name inferred from the EDI file.

path

Source file path.

n_freq

Number of frequency rows read from the impedance section.

tipper

Whether non-empty tipper data are available.

spectra

Whether a spectra section was parsed.

ts

Whether a time-series section was parsed.

For a focused table, request the columns you want:

1station_table = survey.summary(
2    fields=["station", "n_freq", "tipper", "path"],
3)
4
5print(station_table.to_pandas(copy=True))

Example output:

station  n_freq  tipper        path
18-001A      53   False 18-001A.edi
18-002U      53   False 18-002U.edi
18-003A      53   False 18-003A.edi

Save the inventory for a field notebook, spreadsheet review, or processing report:

1inventory.to_csv("survey_inventory.csv", index=False)

Inspect Stations Programmatically#

The APISurvey object behaves like a small collection. You can iterate over loaded EDI objects, select one by name, or access a station by index.

1for edi in survey:
2    print(edi.station, edi.path)
3
4first = survey[0]
5print(first.station)
6
7station = survey.stations[0]
8selected = survey.get_site(station)
9print(selected.path)

Example output:

18-001A
data/AMT/WILLY_DATA/L18PLT/18-001A.edi

Station identifiers can include line prefixes in the survey table while the underlying EDI object may expose the local station name. get_site accepts the common identifiers used by the collection resolver, including station names and file stems.

get_site accepts common station identifiers, including station names and file stems. It returns None by default when a station cannot be resolved:

1maybe_site = survey.get_site("S999")
2if maybe_site is None:
3    print("Station not found")

Output:

Station not found

Handle Parser Errors#

EDI files from different acquisition or processing software can vary in metadata style. For a first pass, keep strict=False and inspect parser errors after loading:

 1survey = read_edis(
 2    "data/AMT/WILLY_DATA/L18PLT",
 3    recursive=False,
 4    strict=False,
 5    progress=False,
 6)
 7
 8errors = survey.errors()
 9print(f"{len(errors)} read error(s)")
10
11for path, exc in errors[:10]:
12    print(path, type(exc).__name__, exc)

The bundled line reads without parser errors:

0 read error(s)

Use strict=True in automated validation jobs when the workflow should fail as soon as one EDI cannot be read:

1survey = read_edis(
2    "data/AMT/WILLY_DATA/L18PLT",
3    recursive=False,
4    strict=True,
5    progress=False,
6)

This stricter mode is useful before committing data to a project archive or before running a batch inversion pipeline.

Choose a Duplicate Policy#

Station names should normally be unique inside one survey or line. When two EDI files resolve to the same station name, on_dup controls which object is kept in the returned survey:

on_dup="replace"

Keep the last station discovered for that name. This is the default and is useful when a later file is the corrected version.

on_dup="keep"

Keep the first station discovered for that name. This is useful when the directory contains temporary exports that should not override the original file.

Example:

1survey = read_edis(
2    "data/AMT/WILLY_DATA",
3    recursive=True,
4    on_dup="keep",
5)
6
7print(survey.stations)

If duplicate names are unexpected, inspect the source paths and fix the station metadata or file selection before continuing.

Control Progress Output#

The progress argument is intended for both notebooks and scripts:

1interactive = read_edis("data/AMT/WILLY_DATA/L18PLT", progress="auto")
2quiet = read_edis("data/AMT/WILLY_DATA/L18PLT", progress=False)
3always_show = read_edis("data/AMT/WILLY_DATA/L18PLT", progress=True)

Use progress=False in tests, scheduled jobs, and log files. Use progress="auto" during interactive survey inspection.

Read Several Sources#

You can pass several folders or files at once. This is helpful when a project has separate field folders but you want one in-memory survey for inspection.

 1survey = read_edis(
 2    [
 3        "data/AMT/WILLY_DATA/L18PLT",
 4        "data/AMT/WILLY_DATA/L22PLT",
 5        "extra_stations/S999.edi",
 6    ],
 7    recursive=True,
 8    on_dup="replace",
 9)
10
11print(survey.summary())

Before moving into QC, a quick filename regularity check can catch mixed exports or accidental files:

Source filename length check for the L18PLT tutorial line.

Keep this merged survey only when the stations belong to the same processing context. For inversion preparation, line identity and profile geometry often matter, so many projects keep one survey object per line.

Use the CLI for Quick Checks#

The command-line interface is useful before opening Python or when you want a small report in a shell workflow.

Show a compact metadata summary:

1pycsamt edi info data/AMT/WILLY_DATA/L18PLT
2pycsamt edi info data/AMT/WILLY_DATA/L18PLT --top 10
3pycsamt edi info data/AMT/WILLY_DATA/L18PLT --format csv

Show station coordinates:

1pycsamt edi stations data/AMT/WILLY_DATA/L18PLT
2pycsamt edi stations data/AMT/WILLY_DATA/L18PLT --sort-by lat
3pycsamt edi stations data/AMT/WILLY_DATA/L18PLT --pattern "18-*" --format csv

Validate EDI structure:

1pycsamt edi validate data/AMT/WILLY_DATA/L18PLT
2pycsamt edi validate data/AMT/WILLY_DATA/L18PLT --no-deep
3pycsamt edi validate data/AMT/WILLY_DATA/L18PLT --format json

Estimate profile geometry when a directory represents one survey line:

1pycsamt edi profile data/AMT/WILLY_DATA/L18PLT
2pycsamt edi profile data/AMT/WILLY_DATA/L18PLT --bearing-method linear --distances

These commands complement the Python API. Use the CLI for fast inspection; use Python when you need reusable processing, filtering, plotting, or export logic.

Move to QC#

After reading the survey, the next step is normally a quality-control table. The survey.collection object can be passed directly to the QC helpers:

 1from pycsamt.emtools.qc import build_qc_table
 2
 3qc = build_qc_table(
 4    survey.collection,
 5    include_skew=True,
 6    recursive=False,
 7    api=True,
 8)
 9
10qc_df = qc.to_pandas(copy=True)
11qc_df.to_csv("survey_qc.csv", index=False)

The QC tutorial explains how to interpret this table and decide which stations need review.

If you want one more visual check before QC, a survey fingerprint compresses phase-tensor behavior across stations and periods:

Quick phase-tensor survey fingerprint for the L18PLT tutorial line.

Troubleshooting#

No stations were loaded

Check that the source path exists, that files use the .edi extension, and that recursive=True is enabled when EDI files are inside line subdirectories.

The station count is smaller than the number of files

Some files may share the same station name. Inspect survey.paths and review on_dup. Use unique station names before final processing.

The reader reports errors but still returns a survey

This is expected with strict=False. Review survey.errors() and decide whether the affected files can be ignored, repaired, or re-exported.

The summary has fewer columns than expected

survey.summary() is intentionally lightweight. Use EDI station, profile, site, QC, or plotting tools for richer diagnostics.

The CLI and Python show different station order

Directory traversal and sorting options can affect display order. Use the CLI --sort-by options or sort the pandas dataframe explicitly when order matters.

Next Steps#

See Also#

Data Formats

Supported data layouts and file-format expectations.

Data Loading

Data loading concepts and reader selection.

EDI Commands

EDI command reference.

pycsamt.api

Public API reference.