pycsamt.zonge.base#

pycsamt.zonge.base#

Foundational building blocks for the Zonge sub-package.

Goals#

  • Provide a single, flexible base every component can inherit from (e.g., Station, Frequency, Resistivity…).

  • Offer a robust AVGFrame holder for data + metadata.

  • Define a clear component contract:
    • from_avg(...) → build from an AVG file/table

    • read(...) → parse & populate attributes

    • write() → reconstruct a textual AVG block

  • Be agnostic to legacy/modern files. Upstream loaders (e.g., utils.load_avg) normalize columns; components consume the tidy frame.

Design notes#

  • All public objects keep string widths short for readability.

  • Columns are lower-case canonical names (e.g., 'freq', 'rho', 'phase', 'comp'…).

  • Components target subsets of the frame and may expose additional, derived attributes internally.

Functions

guess_kind_from_df(df_or_frame[, meta, ...])

Infers the AVG format kind from a DataFrame's columns.

Classes

AVGComponentBase([data, meta, name, verbose])

Abstract base class for a single AVG data component.

AVGFrame(data[, meta, source])

A container for a tidy AVG table and its metadata.

AvgRow(station, freq, comp[, amps, emag, ...])

A structured, format-agnostic representation of a single row.

FieldAliases()

Dynamically provides all known aliases for canonical names.

class pycsamt.zonge.base.FieldAliases[source]#

Bases: object

Dynamically provides all known aliases for canonical names.

This class serves as a convenient, centralized namespace for accessing all known variations of a given canonical column name. It is designed to be a single source of truth for developers who need to work with different possible column names.

Notes

The primary purpose of this class is to improve code readability and maintainability by avoiding the use of “magic strings” for column names. Instead of writing df[("rho", "Resistivity")], a developer can use the more explicit df[FieldAliases.rho].

The class attributes are not hardcoded. They are generated dynamically by introspecting the ALL_ALIASES dictionary from the schema module. This ensures that FieldAliases is always in sync with the master data schema and adheres to the DRY (Don’t Repeat Yourself) principle.

Examples

You can access the tuple of aliases for any canonical name directly as a class attribute:

>>> from pycsamt.zonge.base import FieldAliases
>>> # Get all known names for apparent resistivity
>>> FieldAliases.rho
('ARes.mag', 'Resistivity')
>>> # Get all known names for H-field phase error
>>> FieldAliases.s_hphz
('B.perr', 'H.perr', 'sHphz')

See also

pycsamt.zonge.schema.ALL_ALIASES

The source dictionary used to build this class.

attr_name = 'z_pcterr'#
station: tuple[str, ...] = ('Station', 'Stn')#
freq: tuple[str, ...] = ('Freq', 'Freq.')#
comp: tuple[str, ...] = ('Comp',)#
rho: tuple[str, ...] = ('ARes.mag', 'Resistivity')#
phase: tuple[str, ...] = ('Phase', 'Z.phz')#
aliases = ('Z.%err',)#
amps = ('Amps', 'Tx.Amp')#
canon = 'z.%err'#
coh = ('Choer',)#
e_wgt = ('E.wgt',)#
emag = ('E.mag', 'Emag')#
ephz = ('E.phz', 'Ephz')#
gdp_blk = ('Gdp.Blk',)#
gdp_chn = ('Gdp.Chn',)#
gdp_time = ('Gdp.Time',)#
h_wgt = ('B.wgt', 'H.wgt')#
hmag = ('B.mag', 'H.mag', 'Hmag')#
hphz = ('B.phz', 'Hphz')#
pc_emag = ('%Emag', 'E.%err')#
pc_hmag = ('%Hmag', 'B.%err', 'H.%err')#
pc_rho = ('%Rho', 'ARes.%err')#
rho_sc = ('SRes', 'TMARES', 'TMARES/SRES')#
s_ephz = ('E.perr', 'sEphz')#
s_hphz = ('B.perr', 'H.perr', 'sHphz')#
s_phz = ('Z.perr', 'sPhz')#
skp = ('skp',)#
tx = ('tx',)#
ty = ('ty',)#
z_mwgt = ('Z.mwgt',)#
z_pcterr = ('Z.%err',)#
z_pwgt = ('Z.pwgt',)#
zabs = ('|Z|',)#
zmag = ('Z.mag',)#
class pycsamt.zonge.base.AvgRow(station, freq, comp, amps=None, emag=None, ephz=None, hmag=None, hphz=None, rho=None, phase=None, e_err=None, e_perr=None, h_err=None, h_perr=None, rho_err=None, z_perr=None)[source]#

Bases: object

A structured, format-agnostic representation of a single row.

This dataclass provides a type-safe container for the data from a single measurement record in an AVG file. It serves as a convenient Data Transfer Object (DTO) for operations that require iterating over individual data points, such as testing, serialization to JSON, or specific calculations.

Variables:
  • station (int or float) – The station identifier for the measurement.

  • freq (float) – The frequency at which the measurement was taken (Hz).

  • comp (str) – The component label (e.g., ‘ExHy’). Defaults to ‘ExHy’ if not provided.

  • amps (float, optional) – The transmitter current amplitude (A).

  • ephz (emag,) – The magnitude and phase of the electric field.

  • hphz (hmag,) – The magnitude and phase of the magnetic field.

  • phase (rho,) – The calculated apparent resistivity (ohm-m) and impedance phase (mrad).

  • rho_err (e_err, h_err,) – The relative percent error for E-field, H-field, and resistivity magnitudes.

  • z_perr (e_perr, h_perr,) – The standard deviation (error) for E-field phase, H-field phase, and impedance phase.

Parameters:
station: int | float#
freq: float#
comp: str#
amps: float | None#
emag: float | None#
ephz: float | None#
hmag: float | None#
hphz: float | None#
rho: float | None#
phase: float | None#
e_err: float | None#
e_perr: float | None#
h_err: float | None#
h_perr: float | None#
rho_err: float | None#
z_perr: float | None#
asdict()[source]#
Return type:

dict[str, Any]

class pycsamt.zonge.base.AVGFrame(data, meta=<factory>, source=None)[source]#

Bases: object

A container for a tidy AVG table and its metadata.

This dataclass acts as a standardized wrapper for the data and metadata parsed from a Zonge AVG file. It ensures that data passed between different parts of the package is consistent and self-contained.

The __post_init__ hook automatically standardizes the column names of the incoming DataFrame, ensuring that any instance of AVGFrame always contains a clean, canonical dataset.

Parameters:
  • data (pandas.DataFrame) – A DataFrame containing the tabular data from an AVG file. Column names are automatically normalized to the package’s internal canonical schema upon initialization.

  • meta (dict, optional) – A dictionary holding the header metadata (e.g., survey parameters, units) extracted from the $keyword=value lines in the AVG file.

  • source (pathlib.Path, optional) – The filesystem path to the original .avg file, used for provenance and logging.

Examples

>>> import pandas as pd
>>> from pycsamt.zonge.base import AVGFrame
>>> df = pd.DataFrame({'Resistivity': [100], 'Freq': [1024]})
>>> frame = AVGFrame(data=df, meta={'Survey.Type': 'CSAMT'})
>>> print(frame.data.columns)
Index(['rho', 'freq'], dtype='object')
>>> print(frame.meta['Survey.Type'])
CSAMT

See also

pycsamt.zonge.avg.AVG

The main user-facing data object.

pycsamt.zonge.info.DataInfo

Aggregator that uses AVGFrame.

data: DataFrame#
meta: dict[str, Any]#
source: Path | None#
property nrows: int[source]#

Row count.

property columns: tuple[str, ...][source]#

Column labels (tuple for immutability).

copy()[source]#

Deep copy the frame (safe for mutation).

Return type:

AVGFrame

to_json(*, orient='records', indent=0)[source]#

Serialise data to JSON (metadata excluded).

Parameters:
Return type:

str

meta_as_json(*, indent=0)[source]#

Serialise metadata to JSON.

Parameters:

indent (int)

Return type:

str

asdict()[source]#

Plain dict for diagnostics / logging.

Return type:

dict[str, Any]

class pycsamt.zonge.base.AVGComponentBase(data=None, meta=None, *, name=None, verbose=0)[source]#

Bases: ABC

Abstract base class for a single AVG data component.

This class defines the fundamental “contract” that all data components (e.g., Station, Resistivity, Phase) must follow. It ensures that every component can be initialized from a standardized data source and can serialize itself back into a textual format.

Subclasses are expected to be specialized containers that manage a specific subset of columns from the main AVG DataFrame.

Variables:
  • required (set[str]) – A class attribute specifying the set of canonical column names that must be present in the source DataFrame for the component to be initialized.

  • provides (set[str]) – A class attribute specifying the set of canonical column names that this component is responsible for managing or creating.

  • _frame (pandas.DataFrame) – A protected attribute holding the component’s data as a tidy DataFrame with canonical column names.

  • _meta (dict) – A protected attribute holding relevant metadata.

Parameters:
read(source, meta=None)[source]#

Abstract method to parse a source DataFrame and populate the component’s internal state.

Parameters:
Return type:

None

write()[source]#

Abstract method to serialize the component’s data into a sequence of text lines suitable for an AVG file.

Return type:

Sequence[str]

from_avg(avg, meta=None, \*\*kws)[source]#

A classmethod factory that provides a convenient way to build a component from various sources, including file paths or in-memory DataFrames.

Parameters:
Return type:

AVGComponentBase

Notes

The design of this base class ensures that all components operate on a consistent, tidy data structure. The from_avg factory method is particularly important, as it handles the automatic transformation of legacy AVG files into the modern, canonical format before passing the data to the component’s read method. This makes all components inherently agnostic to the original file format.

required: set[str] = {}#
provides: set[str] = {}#
classmethod from_avg(avg, *, meta=None, **kws)[source]#

Build a component from a path / AVGFrame / dataframe.

The method accepts:
  • path → uses utils.load_avg (modern or legacy)

  • AVGFrame → uses its data/meta directly

  • (df, meta) tuple

  • bare df + explicit meta kwarg

Parameters:
Return type:

AVGComponentBase

abstractmethod read(source, meta=None)[source]#

Parse source and mutate component state.

Implementations should:
  1. validate required columns using _require(...)

  2. copy/select needed columns into self._frame

  3. set/merge metadata into self._meta

Parameters:
Return type:

None

abstractmethod write()[source]#

Serialise the component to text lines. Implementations may delegate to _write_csv_block for a consistent block format (header + CSV). The base does not write to disk.

Return type:

Sequence[str]

property frame: DataFrame[source]#

Read-only view of the component table.

property meta: dict[str, Any][source]#

Free-form metadata.

property name: str[source]#

Short human-readable component name.

property shape: tuple[int, int][source]#

Table shape (rows, cols).

asdict(*, include_meta=True)[source]#

Plain dict with data (+ meta optionally).

Parameters:

include_meta (bool)

Return type:

dict[str, Any]

to_json(*, indent=0)[source]#

JSON serialiser for diagnostics.

Parameters:

indent (int)

Return type:

str

pycsamt.zonge.base.guess_kind_from_df(df_or_frame, meta=None, *, transform=False, mode='soft', error='raise', verbose=False)[source]#

Infers the AVG format kind from a DataFrame’s columns.

This helper inspects the column names of a DataFrame to make an educated guess about whether it represents a legacy (kind-1) or modern (kind-2) AVG file. It can optionally transform legacy data into the modern structure.

Parameters:
  • df_or_frame (pd.DataFrame or AVGFrame) – The DataFrame or AVGFrame object to inspect.

  • meta (mapping, optional) – An optional metadata dictionary. Required if transform is True and the source is a legacy DataFrame.

  • transform (bool, default False) – If True and a legacy (kind-1) format is detected, this function will use the LegacyAVGBase transformer to convert the data into the modern structure.

  • error ({'raise', 'warn', 'ignore'}, default 'raise') – Determines the behavior when a format cannot be reliably determined. - ‘raise’: Raises an AvgFileError. - ‘warn’: Issues a warning and defaults to kind-2. - ‘ignore’: Silently defaults to kind-2.

  • verbose (bool, default False) – Controls logging output during transformation.

  • mode (Literal['strict', 'soft'])

Returns:

  • If transform is False, returns an integer: 1 for legacy, 2 for modern.

  • If transform is True, returns a tuple: (df, meta, kind), where df and meta are the (potentially transformed) data and metadata.

Return type:

int or tuple

Raises:
  • TypeError – If the input is not a pandas DataFrame or AVGFrame.

  • AvgFileError – If error is ‘raise’ and the format cannot be determined.

Notes

The detection logic uses a multi-pass heuristic: 1. It first checks for columns with dot-notation (e.g.,

‘ARes.mag’), which are exclusive to modern files.

  1. If none are found, it checks for legacy-specific QC column names (e.g., ‘%Emag’, ‘sPhz’).

  2. If neither is found, it checks for the presence of internal canonical QC names (e.g., ‘pc_emag’, ‘s_phz’), treating them as modern.

  3. Finally, it falls back to a default controlled by the error parameter.

class pycsamt.zonge.base.LegacyAVGBase(mu0=1.2566370614359173e-06, prefer_emh_over_rho=True)#

Bases: object

Base transformer to convert legacy Zonge *.avg tables (AMTAVG/MTEdit style) into a modern xarray dataset.

The class:
  • normalizes legacy columns → canonical, lower-case names

  • guarantees minimalist coordinates (station, freq, comp)

  • injects modern fields that may be missing in legacy data

  • derives conservative quantities (e.g., |Z| in ohms)

  • composes survey attrs with safe placeholders

Subclass this base to customize any hook:
  • _normalize_columns

  • _ensure_coords

  • _inject_placeholders

  • _derive_safe_quantities

  • _build_attrs

Notes on units#

Legacy files are not always explicit about units. This base assumes the modern CSAVGW conventions when deriving |Z|:

  • E.mag in nV/(m·A) → converted to V/(m·A)

  • B.mag in pT/A → converted to T/A

  • |Z| = E/B in ohms

If only rho and freq exist, we use:
  • |Z| = sqrt(μ0 * ω * ρa) (SI coherent)

We do not synthesize zmag (historic “km/sec”) to avoid mixing conventions. If Z.mag exists, it is preserved.

Resulting Dataset#

The dataset is shaped as:

station × freq × comp

and contains all numeric legacy variables plus modern stubs (weights, errors, rho_sc, etc.), ready for downstream tools.

mu0: float = 1.2566370614359173e-06#
prefer_emh_over_rho: bool = True#
from_path(path, *, utm_zone=None)#

Read a legacy *.avg file and return a dataset.

Parameters:
  • path (str | Path) – Filesystem path to the legacy AVG file.

  • utm_zone (int | None) – Optional UTM zone override passed to load_avg.

Returns:

Multi-dimensional dataset (station × freq × comp).

Return type:

xarray.Dataset

from_dataframe(df, *, meta=None, coords=('station', 'freq', 'comp'), data_vars=None)#

Transform an already-parsed legacy table into a dataset.

Parameters:
  • df (DataFrame) – Tidy legacy data as a pandas.DataFrame.

  • meta (dict[str, Any] | None) – Optional metadata dict (e.g., from load_avg).

  • coords (Sequence[str]) – Coordinate columns to use for gridding.

  • data_vars (Sequence[str] | None) – Optional explicit list of data variables to include. If None, all numeric columns not used as coords are included.

Returns:

Dataset with conservative placeholders in place.

Return type:

xarray.Dataset

to_xarray(obj, *, meta=None, coords=('station', 'freq', 'comp'), data_vars=None, utm_zone=None)#

Convert a legacy table (DataFrame) or a path to a legacy *.avg file into a modern xarray.Dataset.

  • If obj is a path/str → reads the file via from_path.

  • If obj is a DataFrame → forwards to from_dataframe.

Return type:

xarray.Dataset

Parameters:
transform(df, *, meta=None, coords=('station', 'freq', 'comp'), data_vars=None)#

Compatibility alias that mirrors scikit-learn-style transform. Simply forwards to from_dataframe.

Parameters:
Parameters: