pycsamt.zonge#

Zonge AVG, transfer-function, survey, tensor, tipper, processing, QC, and resistivity/phase utilities.

The zonge subpackage provides a comprehensive toolkit for reading, processing, and analyzing Zonge AVG/AMTAVG data files.

class pycsamt.zonge.AVG(verbose=False)#

Bases: BaseAVG

High-level façade for a Zonge AVG/AMTAVG dataset.

An AVG instance loads a raw text file, normalizes its content, and hydrates a full suite of data components, including stations, frequencies, impedance, quality metrics, and header blocks. This makes the entire survey accessible through strongly-typed attributes [1]_.

Parameters:

verbose (bool, default False) – If True, log messages will be printed to the console during file reading and writing operations.

Variables:
  • header (pycsamt.zonge.heads.Header) – Aggregates Hardware, SurveyAnnotation, SurveyConfiguration, and Rx/Tx property blocks.

  • station (pycsamt.zonge.survey.Station) – Manages survey line geometry and station coordinates.

  • frequency (pycsamt.zonge.meas.Frequency) – Manages the frequency axis for the measurements.

  • phase (resistivity,) – Containers for apparent resistivity and phase data.

  • z (pycsamt.zonge.z.Z) – Component for computing the complex impedance tensor.

  • df (pd.DataFrame or None) – The core tidy DataFrame containing all available data columns after parsing and normalization.

from_file(path, verbose=False)#

Classmethod to load and parse an AVG file. This is the primary entry point for creating an AVG instance.

Parameters:
Return type:

AVG

read(source, meta=None)#

Populates the object from a source (file, DataFrame, etc.).

write(path, fmt='auto', \*\*kwargs)#

Writes the data to a file, dispatching to to_modern or to_legacy based on the fmt argument.

to_xarray()#

Exports the entire dataset into a single, comprehensive xarray.Dataset.

Parameters:
to_tensor(var='z', \*\*kwargs)#

Exports a specific variable as a 2x2 NumPy tensor.

Parameters:
Return type:

tuple[ndarray, ndarray, ndarray]

asdict()#

Returns a JSON-serializable dictionary of all header metadata.

Notes

Zonge Engineering produced two AVG file formats:

  • Kind-1 (Legacy): Fixed-width text tables with minimal

    metadata.

  • Kind-2 (Modern): CSV-based format with rich

    $key=value headers, used by modern AMTAVG and ASTATIC.

The loader automatically detects the format and transforms it into a canonical, tidy DataFrame. All subsequent processing is format-agnostic. Zonge placeholders (*) are converted to NaN.

Examples

Load a file, access a component, and write back:

>>> from pycsamt.zonge import AVG
>>> avg = AVG.from_file('LCS01.avg', verbose=True)
>>> # Access resistivity for a specific component
>>> rho_xy = avg.resistivity.frame[
...     avg.resistivity.frame.comp == 'ExHy'
... ]
>>> avg.write('LCS01_clean.avg')

Build from an in-memory DataFrame:

>>> from pycsamt.zonge.utils import load_avg
>>> df, meta = load_avg('raw.avg')
>>> avg = AVG()
>>> avg.read(df, meta=meta)
>>> print(avg.station.span)

References

classmethod from_file(path, *, verbose=False)#

Load and parse an AVG file from a path.

This classmethod is the primary factory for creating a fully populated AVG instance directly from a file on disk. It handles the entire data loading pipeline.

Parameters:
  • path (str or pathlib.Path) – The filesystem path to the .avg file. Both legacy (kind-1) and modern (kind-2) formats are supported.

  • verbose (bool, default False) – If True, log messages will be printed to the console during the file loading and parsing process.

Returns:

A new, fully initialized instance of the AVG class containing all the data and metadata from the file.

Return type:

AVG

Notes

This method is a convenient wrapper around the more general read() method. It creates an instance of the class and then immediately calls obj.read(path) to perform the actual data loading and component population.

Examples

>>> from pycsamt.zonge import AVG
>>> # Load a modern AVG file
>>> avg = AVG.from_file('data/avg/K2.avg', verbose=True)
>>> print(avg.summary)

See also

read

The underlying method that performs the data ingestion.

to_xarray(*, include_qc=True, include_z=True)#

Export the complete dataset to a single xarray.Dataset.

This is the primary export method for creating a comprehensive, multi-dimensional, and self-describing dataset. It aggregates the primary data variables (resistivity, phase), the computed impedance tensor (Z), and all available quality control (QC) metrics into a single object.

Parameters:
  • include_qc (bool, default True) – If True, all available QC components (e.g., pc_emag, s_phz) will be included as data variables in the dataset.

  • include_z (bool, default True) – If True, the real and imaginary parts of the complex impedance tensor (Z) and its propagated error will be included as data variables.

Returns:

A dataset with coordinates (station, freq, comp) and data variables for all included measurements and QC metrics. The dataset’s attributes are populated from the AVG file’s header.

Return type:

xarray.Dataset

Notes

This method provides a high-level, convenient way to get all the processed data into a standard format for analysis and plotting. It gracefully handles cases where optional components (like certain QC metrics) are not available in the source data by skipping them.

Examples

>>> from pycsamt.zonge import AVG
>>> avg = AVG.from_file('data/avg/K2.avg')
>>> # Export all available data
>>> ds = avg.to_xarray()
>>> print(ds.data_vars)
Data variables:
    rho        (station, freq, comp) float64 ...
    phase      (station, freq, comp) float64 ...
    z_real     (station, freq, comp) float64 ...
    ...

See also

to_tensor

Export a single variable to a NumPy tensor.

to_tensor(var='z', *, station=None, agg='mean', fill_value=nan, sort_freq=True, align='union')#

Export a specific variable as a 2x2 tensor.

This is a convenience method that provides direct access to the underlying tensor-shaping capabilities of the data components. It delegates the call to the appropriate component (e.g., Z, Resistivity) to reshape the selected variable into a NumPy array.

Parameters:
  • var ({'z', 'z_real', 'z_imag', 'z_err', 'rho', 'phase'}, default 'z') – The specific data variable to export as a tensor.

  • station (int or float, optional) – If provided, the method returns a 3D tensor for only this station. If None, it returns a 4D tensor containing data for all stations.

  • agg (str or None, default 'mean') – The aggregation method to use if duplicate measurements exist for the same station, frequency, and component.

  • fill_value (float, default np.nan) – The value to use for missing elements in the tensor grid.

  • sort_freq (bool, default True) – If True, the frequency axis of the tensor will be sorted.

  • align ({'union', 'intersection'}, default 'union') – For multi-station tensors, determines how to handle different frequency sets between stations.

Returns:

  • tensor (numpy.ndarray) – The data reshaped into a tensor. Shape is (n_freq, 2, 2) for a single station or (n_station, n_freq, 2, 2) for multiple stations.

  • freqs (numpy.ndarray) – The frequency axis for the tensor.

  • stations (numpy.ndarray) – The station axis for the tensor (empty for a single- station request).

Return type:

tuple[ndarray, ndarray, ndarray]

See also

to_xarray

Export the entire dataset to a labeled xarray object.

pycsamt.zonge.tensor.TensorBase.to_tensor

The underlying implementation.

property header#

Access the Header component.

property station#

Access the Station component.

property z#

Access the impedance (Z) component.

property resistivity#

Access the Resistivity component.

property phase#

Access the Phase component.

property frequency#

Access the Frequency component.

property df: DataFrame | None#

Access the core tidy DataFrame containing all available data columns after parsing and normalization.

fit(source, meta=None)#

Read, parse, and populate components from a data source.

This is the primary data ingestion method for the class. It is designed to be flexible, accepting various data sources and orchestrating the entire process of parsing, transforming, and populating the internal data components.

Parameters:
  • source (str, Path, AVGFrame, or pd.DataFrame) –

    The data source to load. This can be:

    • A string or pathlib.Path pointing to a Zonge AVG file (both legacy and modern formats are supported).

    • A pandas.DataFrame containing AVG data. Note that if the DataFrame is not already standardized, it will be automatically standardized by AVGFrame.

    • An existing AVGFrame object.

  • meta (mapping, optional) – An optional dictionary of metadata. This is primarily used when source is a pd.DataFrame to provide header-like information.

Returns:

self – The method returns the instance of the class, allowing for convenient method chaining.

Return type:

BaseAVG

Raises:
  • TypeError – If the source is of an unsupported type.

  • FileNotFoundError – If source is a path that does not exist.

  • AvgFileError – If a file source cannot be reliably classified as either legacy (kind-1) or modern (kind-2).

  • AvgDataError – If parsing fails due to malformed or missing data.

Notes

The read method encapsulates a complete data processing pipeline:

  1. File Handling: If a path is provided, it reads the file and uses classify_avg_format() to determine the file kind.

  2. Transformation: If a legacy (kind-1) file is detected, it automatically uses the LegacyAVGBase transformer to convert the data to a modern, structured format.

  3. Standardization: If a raw pd.DataFrame is provided, it is wrapped in an AVGFrame, which triggers the automatic standardization of column names to the internal canonical schema.

  4. Component Population: The final, clean DataFrame and metadata are passed to the DataInfo component, which in turn populates all the individual data components (e.g., Station, Z, Resistivity).

Examples

>>> from pycsamt.zonge import AVG
>>> # Read from a modern file
>>> avg = AVG().read('data/avg/K2.avg')
>>>
>>> # Read from a legacy file (will be auto-transformed)
>>> avg_legacy = AVG().read('data/avg/K1.avg')
>>>
>>> # Read from an in-memory DataFrame
>>> import pandas as pd
>>> df = pd.DataFrame({'Freq': [1024], 'Resistivity': [100]})
>>> avg_from_df = AVG().read(df)
class pycsamt.zonge.AMTAVG(verbose=False)#

Bases: AVG

Extends AVG with tensor components and analytical methods.

This class inherits from AVG and provides a more specialized, analysis-focused interface. It is designed for users who need to perform advanced geophysical data processing and manipulation. It is designed for working directly with specific tensor elements (e.g., z_xy, rho_yx).

The primary features of this class include:

  • Direct, property-based access to individual components of the impedance, resistivity, and phase tensors as pandas Series (e.g., z_xy, res_yx_err).

  • A suite of methods for advanced data processing, such as tensor rotation, phase unwrapping, and statistical analysis.

In addition to providing direct access to tensor components, this class includes methods for advanced data manipulation, such as recalculating resistivity and phase from the impedance tensor.

Variables:
Parameters:

verbose (bool)

compute_resistivity_phase()#

Calculates apparent resistivity and phase from the complex impedance tensor Z.

Parameters:

todeg (bool)

Return type:

tuple[Series, Series, Series, Series]

set_resistivity_phase(rho, phi, rho_err=None, phi_err=None)#

Updates the dataset with new resistivity and phase values, triggering a recalculation of the impedance tensor.

Parameters:
get_tensor_by_station(station_id, var='z')#

Fetches a 3D tensor for a single station using xarray.

Parameters:
calculate_statistics()#

Computes statistical QC metrics from repeated measurements.

Parameters:
  • drop_on_failure (bool)

  • update_components (bool)

Return type:

DataFrame

unwrap_phase()#

Corrects for 2π phase wrapping in the impedance phase data.

Parameters:
Return type:

DataFrame

rotate(angle_deg)#

Performs a 2D rotation of the impedance tensor.

Parameters:
calculate_tipper(hz_data)#

Computes the Tipper transfer function from vertical field data.

Parameters:
Return type:

DataFrame

Examples

>>> from pycsamt.zonge import AMTAVG
>>> amt_avg = AMTAVG.from_file('data/avg/K2.avg')
>>> # Rotate the data by 30 degrees clockwise
>>> amt_avg.rotate(30)
>>> # Access the newly rotated Z_xy component
>>> z_xy_rotated = amt_avg.z_xy

See also

AVG

The parent class providing the main data loading and exporting functionality.

Z

The component that handles impedance calculations.

Resistivity

The component that manages resistivity data.

Phase

The component that manages phase data.

compute_resistivity_phase(todeg=False)#

Compute rho and phi from the complex impedance Z.

This method calculates the apparent resistivity (\(\rho_a\)) and impedance phase (\(\phi\)) from the complex impedance tensor (\(\mathbf{Z}\)). It also propagates the errors from \(\mathbf{Z}\) to the new values.

Parameters:

todeg (bool, default False) – If True, the output impedance phase and its error will be converted from milliradians to degrees.

Returns:

  • rho (pandas.Series) – The calculated apparent resistivity in ohm-meters.

  • phi (pandas.Series) – The calculated impedance phase in milliradians or degrees.

  • rho_err (pandas.Series) – The propagated relative error in apparent resistivity (in percent).

  • phi_err (pandas.Series) – The propagated error in impedance phase (in the same units as phi).

Return type:

tuple[Series, Series, Series, Series]

Notes

The calculations are based on the fundamental MT relationships [1]_:

\[\rho_a = \frac{1}{\omega \mu_0} |Z|^2 \quad \text{and} \quad \phi = \arctan\left(\frac{\text{Im}(Z)}{\text{Re}(Z)}\right)\]

Error propagation for \(\rho_a\) is given by:

\[\frac{\delta\rho_a}{\rho_a} = 2 \frac{\delta|Z|}{|Z|}\]

References

set_resistivity_phase(rho, phi, rho_err=None, phi_err=None)#

Attach new rho/phi data and reconstruct Z.

This method updates the core DataFrame with new apparent resistivity and impedance phase values. After updating, it automatically re-initializes all data components, ensuring that dependent values (like the impedance tensor Z) are recalculated and the entire object state remains consistent.

Parameters:
  • rho (pandas.Series) – A Series containing the new apparent resistivity values. The index must align with the internal DataFrame.

  • phi (pandas.Series) – A Series containing the new impedance phase values, assumed to be in milliradians.

  • rho_err (pandas.Series, optional) – A Series containing the new relative error values for resistivity (in percent).

  • phi_err (pandas.Series, optional) – A Series containing the new error values for phase (in milliradians).

Notes

This is a powerful method for data manipulation, such as applying static shifts or corrections. By calling self.info.read at the end, it ensures that all changes are propagated correctly throughout the entire data model.

get_tensor_by_station(station_id, *, var='z')#

Fetch a 3D tensor for a single station using xarray.

This method extracts the data for a single station from the full multi-station dataset and returns it as a 3D tensor in an xarray.DataArray.

Parameters:
  • station_id (int or float) – The unique identifier for the station to retrieve. This must match one of the values in the ‘station’ coordinate of the dataset.

  • var ({'z', 'rho', 'phase'}, default 'z') – The specific data variable to retrieve. - ‘z’: Complex impedance tensor. - ‘rho’: Apparent resistivity tensor. - ‘phase’: Impedance phase tensor.

Returns:

A 3D DataArray with dimensions (frequency, e, h) containing the tensor data for the requested station.

Return type:

xarray.DataArray

Raises:
  • KeyError – If the specified station_id is not found in the dataset’s station coordinates.

  • ValueError – If an unsupported var is requested.

  • NotReadError – If this method is called before data has been loaded.

Notes

This method leverages the powerful label-based indexing of xarray’s .sel() method. It first constructs the full multi-station tensor in memory and then selects the slice corresponding to the given station_id.

Examples

>>> from pycsamt.zonge import AMTAVG
>>> avg = AMTAVG.from_file('data/avg/K2.avg')
>>> # Get the resistivity tensor for station 25.0
>>> rho_tensor_25 = avg.get_tensor_by_station(25.0, var='rho')
>>> print(rho_tensor_25.shape)
(28, 2, 2)
calculate_statistics(*, drop_on_failure=True, update_components=True)#

Calculate statistics from grouped data.

This method computes statistical metrics for repeated measurements, mirroring the process of the original Zonge AMTAVG program. It groups the data by station and frequency and calculates the mean, standard deviation (σ), and coefficient of variation (C-var) for key measurement columns.

Parameters:
  • drop_on_failure (bool, default True) – If True, rows where statistical calculations cannot be performed (e.g., only one measurement in a group) will be excluded from the output DataFrame.

  • update_components (bool, default True) – If True, after calculating the statistics, this method will automatically update the corresponding QC components (e.g., self.pc_rho, self.s_phz) with the newly computed values.

Returns:

A DataFrame containing the calculated statistical metrics, including mean, standard deviation, and coefficient of variation for rho, phase, emag, and hmag.

Return type:

pandas.DataFrame

Notes

The calculations are based on the formulas described in the AMTAVG manual [1]_. The coefficient of variation (C-var), which corresponds to the legacy % columns, is calculated as:

\[C_{var} = 100 \cdot \frac{\sigma}{\bar{x}}\]

where \(\sigma\) is the standard deviation and \(\bar{x}\) is the mean of the measurements.

References

unwrap_phase(*, todeg=False, update_components=True)#

Unwraps the impedance phase to correct for 2π jumps.

This method implements a phase referencing/unwrapping algorithm, similar to the PHASEREF mode in the original Zonge AMTAVG program. It corrects for phase wrapping by ensuring that the absolute difference between consecutive phase values (sorted by frequency) does not exceed π radians (1000π milliradians).

Parameters:
  • todeg (bool, default False) – If True, the final unwrapped phase values in the DataFrame will be converted from milliradians to degrees.

  • update_components (bool, default True) – If True, the main DataFrame (self.info.df) is updated in place with the unwrapped phase values, and all data components are re-read to reflect this change.

Returns:

A new DataFrame with the unwrapped phase values. The main self.info.df is also updated if update_components is True.

Return type:

pandas.DataFrame

Notes

The unwrapping is performed independently for each unique station and component pair. The data is first sorted by frequency to ensure the correct sequence for unwrapping.

The core of the algorithm uses numpy.unwrap(), which operates in radians. The phase values, assumed to be in milliradians, are converted to radians before unwrapping and then converted back to the desired output unit.

Examples

>>> from pycsamt.zonge import AMTAVG
>>> avg = AMTAVG.from_file('data/avg/K2.avg')
>>> # Unwrap phase and update the object in place
>>> avg.unwrap_phase()
>>> # Get the unwrapped phase for the xy component
>>> unwrapped_phi_xy = avg.phase_xy

See also

numpy.unwrap

The underlying function used for unwrapping.

rotate(angle, *, update_components=True)#

Rotate the impedance tensor by a given angle.

This method performs a 2D rotation of the impedance tensor \(\mathbf{Z}\) for all measurements. The rotation is applied in a clockwise-positive direction, which is standard in many geophysical applications.

Parameters:
  • angle (float) – The rotation angle in degrees, where a positive angle corresponds to a clockwise rotation of the coordinate system.

  • update_components (bool, default True) – If True, the main DataFrame (self.info.df) is updated in place with the new, rotated impedance values, and all dependent components (like resistivity and phase) are recalculated and updated.

Returns:

A new DataFrame containing the four rotated complex impedance components (z_xx_rot, z_xy_rot, z_yx_rot, z_yy_rot). The main self.info.df is also updated if update_components is True.

Return type:

pandas.DataFrame

Notes

The rotation is performed using the standard 2D tensor rotation matrix [1]_:

\[\mathbf{Z'} = \mathbf{R}(\theta) \mathbf{Z} \mathbf{R}^T(\theta)\]

where \(\mathbf{Z}\) is the original impedance tensor, \(\mathbf{Z'}\) is the rotated tensor, \(\theta\) is the rotation angle, and \(\mathbf{R}(\theta)\) is the rotation matrix:

\[\begin{split}\mathbf{R}(\theta) = \begin{pmatrix} \cos\theta & \sin\theta \\ -\sin\theta & \cos\theta \end{pmatrix}\end{split}\]

This operation updates the z_xx, z_xy, z_yx, and z_yy components. If update_components is True, the apparent resistivity and phase are then recalculated from this new rotated impedance tensor.

References

See also

compute_resistivity_phase

Recalculates rho and phi from Z.

calculate_tipper(hz_data, *, update_components=True)#

Calculate the Tipper transfer function.

This method computes the complex Tipper components (Tx, Ty) by solving the linear relationship: \(H_z = T_x H_x + T_y H_y\)

Parameters:
  • hz_data (pandas.Series) – A Series containing the complex vertical magnetic field (\(H_z\)) data. The index of this Series must match the index of the main DataFrame (self.info.df).

  • update_components (bool, default True) – If True, a new Tipper component will be created and attached to the self.info object, populated with the calculated Tx and Ty values.

Returns:

A new DataFrame containing the calculated complex Tipper components, tx and ty.

Return type:

pandas.DataFrame

Notes

The calculation is performed for each unique station and frequency using a least-squares approach to solve for \(T_x\) and \(T_y\). This requires at least two linearly independent horizontal field measurements for a stable solution.

This method requires the horizontal magnetic field data (hmag, hphz) to be present in the dataset.

property z_xx#
property z_xy#
property z_yx#
property z_yy#
property z_xx_err#
property z_xy_err#
property z_yx_err#
property z_yy_err#
property res_xx#
property res_xy#
property res_yx#
property res_yy#
property res_xx_err#
property res_xy_err#
property res_yx_err#
property res_yy_err#
property phase_xx#
property phase_xy#
property phase_yx#
property phase_yy#
property phase_xx_err#
property phase_xy_err#
property phase_yx_err#
property phase_yy_err#
class pycsamt.zonge.BaseAVG(verbose=False)#

Bases: Zonge

Base class for AVG data handling and file writing.

This class serves as the core engine for reading, parsing, and writing Zonge AVG data files. It handles the logic for identifying file types (legacy vs. modern), transforming legacy data, and populating a structured data model.

Parameters:

verbose (bool, default False) – If True, log messages will be printed to the console during file reading and writing operations, providing insight into the process.

Variables:
  • info (DataInfo) – An aggregator object that holds all the individual data components (e.g., Header, Station, Z, Resistivity).

  • verbose (bool) – Controls the verbosity of logging output.

  • _kind ({1, 2} or None) – Indicates the detected file format: 1 for legacy, 2 for modern. None if no file has been read.

  • _source_path (pathlib.Path or None) – The path to the source file that was read.

Notes

This class is typically not instantiated directly by users. Instead, the AVG class, which inherits from BaseAVG, is the primary user-facing entry point.

The read method is the main entry point for data ingestion and is designed to be flexible, accepting file paths or in-memory data structures.

Examples

While direct use is uncommon, one could use BaseAVG like so:

>>> from pycsamt.zonge.avg import BaseAVG
>>> avg_processor = BaseAVG(verbose=True)
>>> avg_processor.read('path/to/your/data.avg')
>>> avg_processor.write('path/to/output.avg', fmt='modern')

See also

AVG

The primary user-facing class for AVG data.

DataInfo

The main component aggregator used by this class.

read(source, meta=None)#

Read, parse, and populate components from a data source.

This is the primary data ingestion method for the class. It is designed to be flexible, accepting various data sources and orchestrating the entire process of parsing, transforming, and populating the internal data components.

Parameters:
  • source (str, Path, AVGFrame, or pd.DataFrame) –

    The data source to load. This can be:

    • A string or pathlib.Path pointing to a Zonge AVG file (both legacy and modern formats are supported).

    • A pandas.DataFrame containing AVG data. Note that if the DataFrame is not already standardized, it will be automatically standardized by AVGFrame.

    • An existing AVGFrame object.

  • meta (mapping, optional) – An optional dictionary of metadata. This is primarily used when source is a pd.DataFrame to provide header-like information.

Returns:

self – The method returns the instance of the class, allowing for convenient method chaining.

Return type:

BaseAVG

Raises:
  • TypeError – If the source is of an unsupported type.

  • FileNotFoundError – If source is a path that does not exist.

  • AvgFileError – If a file source cannot be reliably classified as either legacy (kind-1) or modern (kind-2).

  • AvgDataError – If parsing fails due to malformed or missing data.

Notes

The read method encapsulates a complete data processing pipeline:

  1. File Handling: If a path is provided, it reads the file and uses classify_avg_format() to determine the file kind.

  2. Transformation: If a legacy (kind-1) file is detected, it automatically uses the LegacyAVGBase transformer to convert the data to a modern, structured format.

  3. Standardization: If a raw pd.DataFrame is provided, it is wrapped in an AVGFrame, which triggers the automatic standardization of column names to the internal canonical schema.

  4. Component Population: The final, clean DataFrame and metadata are passed to the DataInfo component, which in turn populates all the individual data components (e.g., Station, Z, Resistivity).

Examples

>>> from pycsamt.zonge import AVG
>>> # Read from a modern file
>>> avg = AVG().read('data/avg/K2.avg')
>>>
>>> # Read from a legacy file (will be auto-transformed)
>>> avg_legacy = AVG().read('data/avg/K1.avg')
>>>
>>> # Read from an in-memory DataFrame
>>> import pandas as pd
>>> df = pd.DataFrame({'Freq': [1024], 'Resistivity': [100]})
>>> avg_from_df = AVG().read(df)
add_topography(stn_file, utm_zone=None, epsg=None)#

Read and attach station topography data.

This method reads a Zonge .stn file (or a DataFrame with the same structure) and attaches a populated Topography object to the instance.

Parameters:
  • stn_file (str, Path, or DataFrame) – The path to the .stn file or a pre-loaded DataFrame containing the station location data.

  • utm_zone (str)

  • epsg (int)

Returns:

self – The method returns the instance, allowing for method chaining.

Return type:

BaseAVG

to_modern(path=None, *, stamp=True, float_fmt='%.6g', na_rep='*', header_spaces=False)#

Write data to a modern (kind-2) AVG file.

This method serializes the object’s data into a modern, CSAVGW-style .avg file. It delegates the core writing and formatting logic to the write_avg() function.

Parameters:
  • path (str or pathlib.Path, optional) – The output file path. If None, a default filename (e.g., K2_modern.avg) is created in the current working directory.

  • stamp (bool, default True) – If True, a $Written=<timestamp> line is added to the header for provenance.

  • float_fmt (str, default "%.6g") – The format specifier for floating-point numbers.

  • na_rep (str, default "*") – The string representation for missing (NaN) values.

  • header_spaces (bool, default False) – If True, adds spaces around the equals sign in header keywords (e.g., $Key = Value).

Notes

This method intelligently prepares the data for writing by: 1. Assembling a complete header by calling the

to_keywords() method on the header component.

  1. Generating a file banner from the hardware component.

  2. Filtering out any artificial data rows (e.g., those with NaN in the ‘rho’ column) that may have been created during a legacy-to-modern transformation.

See also

to_legacy

Write data to a legacy (kind-1) file.

pycsamt.zonge.utils.write_avg

The underlying file writing function.

to_legacy(path=None, *, precision=4, na_rep='*')#

Write data to a legacy (kind-1) AVG file.

This method reconstructs the classic, fixed-width format of older Zonge AVG files. It uses the populated data components to generate the header and formatted data rows.

Parameters:
  • path (str or pathlib.Path, optional) – The output file path. If None, a default filename (e.g., K2_legacy.avg) is created in the current working directory.

  • precision (int, default 4) – The number of decimal places for scientific notation fields (e.g., ‘Emag’, ‘Resistivity’).

  • na_rep (str, default "*") – The string representation for missing (NaN) values.

Notes

The conversion from a modern, comprehensive data structure to the legacy format involves several key steps:

  • Header Synthesis: It creates a minimal legacy header using $ASPACE and $XMTR keywords derived from the header component.

  • Column Translation: It translates the internal canonical column names (e.g., ‘pc_emag’) back to their legacy equivalents (e.g., ‘%Emag’) for correct formatting.

  • Data Filtering: It first filters out any artificial data rows (e.g., those with NaN in the ‘rho’ column) to ensure the output is clean.

Be aware that some information from a modern dataset may be lost in this conversion, as the legacy format does not support all the fields of the modern format.

See also

to_modern

Write data to a modern (kind-2) file.

write(path=None, *, fmt='auto', **kwargs)#

Write AVG data to a file in the specified format.

This method acts as a high-level dispatcher, calling either the to_legacy() or to_modern() method based on the fmt parameter. It provides a single, convenient entry point for file serialization.

Parameters:
  • path (str or pathlib.Path, optional) – The output file path. If None, a default filename will be generated based on the source file’s name.

  • fmt ({'legacy', 'kind1', 'modern', 'kind2', 'auto'}, default 'auto') – The desired output format. - ‘legacy’ or ‘kind1’: Writes a fixed-width legacy file. - ‘modern’ or ‘kind2’: Writes a CSV-based modern file. - ‘auto’: Defaults to the modern format.

  • **kwargs – Additional keyword arguments to be passed to the specific writing method (to_legacy or to_modern). For example, precision for to_legacy or float_fmt for to_modern.

Raises:
  • ValueError – If an unsupported fmt is specified.

  • NotReadError – If this method is called before data has been loaded.

See also

to_legacy

Writes data to a legacy (kind-1) file.

to_modern

Writes data to a modern (kind-2) file.

property summary#

Provide a Bunch object with a summary of key attributes.

This property aggregates the most important descriptive metadata and statistics from the loaded dataset into a single, easy-to-read object. It is useful for quickly inspecting the contents and overall scope of the survey data.

Returns:

A Bunch object (which behaves like a dictionary with attribute-style access) containing key information such as the source filename, data format kind, project name, number of stations and frequencies, and their respective ranges.

Return type:

pycsamt.api.bunch.Bunch

Notes

The summary property is read-only and is generated on-demand by introspecting the various data components (e.g., header, station, frequency). If no data has been loaded, it will return a Bunch with a ‘status’ key indicating this.

The Bunch object has a user-friendly string representation, making it ideal for printing in interactive sessions.

Examples

>>> from pycsamt.zonge import AVG
>>> avg = AVG.from_file('data/avg/K2.avg')
>>> print(avg.summary)
<Bunch object with keys: source_file, data_kind, project, ...>
>>> avg.summary.num_stations
28
asdict()#

Return a shallow, JSON-serializable representation.

This method aggregates the keyword dictionaries from all header components into a single dictionary, providing a complete, serializable view of the survey’s metadata.

Returns:

A dictionary containing all header information.

Return type:

dict

class pycsamt.zonge.Zonge(verbose=False, **kws)#

Bases: object

A foundational base class for Zonge data objects.

This class provides common attributes and methods, such as verbosity control, a standardized logger, and parameter management, that can be inherited by other classes in the Zonge subpackage.

Parameters:
  • verbose (bool, default False) – Controls the level of detail in logging output. If True, informational and debugging messages will be displayed.

  • **kws (dict) – Additional keyword arguments for future compatibility.

get_params(deep=True)#

Get parameters for this object.

This method retrieves the parameters that were set in the object’s __init__ method.

Parameters:

deep (bool, default True) – If True, will recursively return the parameters for contained sub-objects.

Returns:

A dictionary of parameter names mapped to their values.

Return type:

dict

set_params(**params)#

Set the parameters of this object.

This method allows for updating the object’s parameters after it has been instantiated.

Parameters:

**params (dict) – A dictionary of parameter names and their new values.

Returns:

self – The instance with updated parameters.

Return type:

object

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

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#

Row count.

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

Column labels (tuple for immutability).

copy()#

Deep copy the frame (safe for mutation).

Return type:

AVGFrame

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

Serialise data to JSON (metadata excluded).

Parameters:
Return type:

str

meta_as_json(*, indent=0)#

Serialise metadata to JSON.

Parameters:

indent (int)

Return type:

str

asdict()#

Plain dict for diagnostics / logging.

Return type:

dict[str, Any]

class pycsamt.zonge.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)#

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()#
Return type:

dict[str, Any]

class pycsamt.zonge.FieldAliases#

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.AVGComponentBase(data=None, meta=None, *, name=None, verbose=0)#

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)#

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

Parameters:
Return type:

None

write()#

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)#

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)#

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)#

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()#

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#

Read-only view of the component table.

property meta: dict[str, Any]#

Free-form metadata.

property name: str#

Short human-readable component name.

property shape: tuple[int, int]#

Table shape (rows, cols).

asdict(*, include_meta=True)#

Plain dict with data (+ meta optionally).

Parameters:

include_meta (bool)

Return type:

dict[str, Any]

to_json(*, indent=0)#

JSON serialiser for diagnostics.

Parameters:

indent (int)

Return type:

str

class pycsamt.zonge.DataInfo(verbose=False)#

Bases: Zonge

High-level aggregator for a complete Zonge AVG dataset.

This class acts as the primary container and orchestrator for all data and metadata parsed from a Zonge AVG file. It composes all other data components (e.g., Header, Z, Resistivity, Phase, and QC metrics) into a single, convenient object.

Its main role is to provide a unified interface, holding all the structured information in one place after the initial parsing is complete.

Variables:
  • df (pandas.DataFrame or None) – The core tidy DataFrame containing all available data columns after parsing and standardization.

  • meta (mapping or None) – The raw metadata dictionary extracted from the file’s header section.

  • header (Header) – A component that aggregates all header-level metadata.

  • station (Station) – A component that manages survey line geometry.

  • z (Z) – The component for computing the complex impedance tensor.

  • phase (resistivity,) – Components for apparent resistivity and phase data.

  • comp (frequency, amps,) – Components for core measurement quantities.

  • pc_rho (pc_emag, pc_hmag,) – Components for percent-error quality control metrics.

  • s_phz (s_ephz, s_hphz,) – Components for phase standard deviation QC metrics.

Parameters:

verbose (bool)

from_avg(avg, meta=None)#

A classmethod factory to build a DataInfo object from various sources, including a file path or DataFrame.

Parameters:
Return type:

DataInfo

read(source, meta=None)#

Orchestrates the population of all sub-components from a standardized DataFrame and metadata dictionary.

Parameters:
Return type:

None

Notes

The read method is the core of this class. It iterates through all its component attributes and calls their respective read methods. It includes a robust error- handling mechanism that will issue a warning and skip any component that fails to load (e.g., due to missing data in the source file), making the loading process resilient.

Examples

While typically used internally by the AVG class, you could use DataInfo directly:

>>> from pycsamt.zonge.info import DataInfo
>>> from pycsamt.zonge.utils import load_avg
>>> df, meta = load_avg('data/avg/K2.avg')
>>> data_info = DataInfo()
>>> data_info.read(df, meta)
>>> print(data_info.station)
Station(n=28, span=25.0–1375.0 m, inc=50.0)

See also

pycsamt.zonge.avg.AVG

The main user-facing class that uses DataInfo.

pycsamt.zonge.base.AVGComponentBase

The base class for all components held by DataInfo.

classmethod from_avg(avg, *, meta=None)#

Build a DataInfo object from a path, AVGFrame, or DataFrame.

Parameters:
Return type:

DataInfo

read(source, meta=None)#

Orchestrate reading data into all sub-components.

Parameters:
Return type:

None

class pycsamt.zonge.Amps(data=None, meta=None, *, name=None)#

Bases: AVGComponentBase

Transmitter current amplitude container (unit: A).

The class normalises the amps column to numeric, tracks a few useful stats, and can (optionally) export itself as a small CSV block. Context columns are preserved.

Examples

>>> amps = Amps.from_avg((df, meta))
>>> amps.stats.mean
12.7
>>> ds = amps.to_xarray()   # optional grid for convenience
Parameters:
required: set[str] = {}#
provides: set[str] = {'amps'}#
read(source, meta=None)#

Parse source and populate the amps column as float.

Non-numeric entries (‘*’, blanks) become NaN. Context columns (station/freq/comp) are kept when present.

Parameters:
Return type:

None

property stats: _AmpStats#

Return min/max/mean/median/count snapshot.

as_series()#

Return the amps column as a Series (copy).

Return type:

Series

to_frame()#

Return a small table with context + amps only.

Return type:

DataFrame

to_xarray(*, coords=('station', 'freq', 'comp'), attrs=None)#

Optional convenience: grid the column into an xarray dataset (dims: station × freq × comp) with a single data-variable called amps.

Parameters:
write()#

Serialise as a compact CSV fragment. We keep context columns if present so the block remains useful alone.

Return type:

Sequence[str]

class pycsamt.zonge.CompMeas(data=None, meta=None, *, name=None, verbose=0)#

Bases: AVGComponentBase

Enumeration/validator for classical CSAMT component labels.

The class guarantees a canonical comp column with allowed values (case-sensitive): {'ExHy','ExHx','EyHy','EyHx'}. A few common case variants are accepted and normalized.

Typical usage#

>>> cm = CompMeas.from_avg((df, meta))
>>> cm.unique
['ExHy']  # for a scalar survey with one component
required: set[str] = {}#
provides: set[str] = {'comp'}#
read(source, meta=None)#

Ensure a normalised comp column exists.

  • If missing, default to ‘ExHy’ (legacy kind-1 style).

  • If present, normalise values and validate membership.

Parameters:
Return type:

None

write()#

Serialise the component block as a compact CSV fragment.

Only the context columns and comp are emitted to keep files readable. Disk I/O is the caller’s responsibility.

Return type:

Sequence[str]

property unique: list[str]#

Sorted unique component labels.

Parameters:
class pycsamt.zonge.Frequency(data=None, meta=None, *, name=None)#

Bases: AVGComponentBase

Frequency axis manager (Hz) for AVG tables.

Goals#

  • Read from legacy and modern frames (column aliases handled)

  • Enforce positivity (> 0 Hz) while tolerating missing markers

  • Offer stable unique grids across stations/components

  • Provide a compact to_xarray() for downstream use

Notes

  • Legacy decimals like ‘.5’ are parsed correctly.

  • Missing values (‘*’, ‘’, None) become NaN and are ignored in summaries. Non-positive numeric entries raise FrequencyError.

required: set[str] = {}#
provides: set[str] = {'freq'}#
read(source, meta=None, **kws)#

Load frequency values from a tidy frame or a flat vector.

If source is a DataFrame, we try to keep station / comp when present. Otherwise we inject conservative defaults: station=NaN, comp=’ExHy’.

Parameters:
Return type:

None

write()#

Emit a compact CSV block with the contextual columns that we manage (station, freq, comp), suitable for round-tripping.

Return type:

list[str]

unique(*, sort=True, dropna=True, rtol=1e-06, atol=1e-12)#

Unique global frequency grid with tolerance deduplication.

Parameters:
Return type:

ndarray

by_station(*, rtol=1e-06, atol=1e-12)#

Per-station unique frequency grids (sorted).

Parameters:
Return type:

dict[float, ndarray]

property n_unique: int#

Number of unique frequencies in the table.

static logspace(decade_start, decade_stop, n_points)#

Canonical log-spaced grid (10**start → 10**stop), inclusive.

Parameters:
  • decade_start (int)

  • decade_stop (int)

  • n_points (int)

Return type:

ndarray

to_xarray(*, coords=('station', 'freq', 'comp'), attrs=None)#

Convert to an xarray.Dataset. We include freq as a data variable as well, which is helpful in some consumers; the coordinate is still the same freq axis created by coords.

Parameters:
Parameters:
class pycsamt.zonge.ASTATIC(avg_data=None, verbose=False, **kws)#

Bases: Zonge

A class for advanced processing of Zonge AVG data.

This class provides a suite of methods for data conditioning and analysis that mirror the functionality of Zonge’s ASTATIC software [1]_. It operates on a loaded AMTAVG object, allowing for complex operations like static shift correction, data filtering, and interpolation.

Parameters:
  • avg_data (BaseAVG, optional) – An initialized and loaded AVG object. If provided, the processor is immediately ready for use.

  • verbose (bool, default False) – If True, log messages will be printed to the console during processing operations, providing insight into the process.

Variables:

avg (AMTAVG or None) – The AVG data object that the processor is operating on. It is populated by either passing it to the constructor or by using the read() method.

read(source, meta=None)#

Loads a data source into the processor.

Parameters:
Return type:

ASTATIC

correct_static_shift(...)#

Corrects for static shift using various spatial filters.

Parameters:
  • reference_freq (float)

  • filter_method (Literal['tma', 'flma', 'ama'])

  • update_components (bool)

Return type:

DataFrame

correct_capacitive_coupling(...)#

Remediates high-frequency distortions from capacitive coupling.

Parameters:
Return type:

DataFrame

Notes

The ASTATIC class is designed using a “composition over inheritance” approach. It is not a type of AVG file; rather, it is a tool that contains and operates on an AVG object.

Most processing methods, such as correct_static_shift, modify the underlying avg object’s DataFrame in place when the update_components parameter is set to True. This ensures that all data components are consistently updated after a processing step.

Examples

The typical workflow involves loading data with an AMTAVG object and then passing that object to the ASTATIC processor.

>>> from pycsamt.zonge import AMTAVG, ASTATIC
>>> # 1. Load the data
>>> avg = AMTAVG.from_file('data/avg/K2.avg')
>>>
>>> # 2. Create a processor and apply a correction
>>> processor = ASTATIC().read(avg)
>>> processor.correct_static_shift(
...     reference_freq=4096, filter_method='tma'
... )
>>>
>>> # 3. The original avg object is now updated and can be saved
>>> avg.to_modern('K2_corrected.avg')

References

See also

pycsamt.zonge.avg.AMTAVG

The primary data container class.

pycsamt.zonge.proc_utils

The module containing the core filtering algorithms.

read(source, meta=None)#

Load a data source into the processor.

This is the primary method for associating a dataset with the ASTATIC processor. It is designed to be flexible, accepting various input types and ensuring that the processor is initialized with a valid, fully-loaded AVG data object.

Parameters:
  • source (str, Path, AVGFrame, pd.DataFrame, or BaseAVG) –

    The data source to load. This can be:

    • A string or pathlib.Path pointing to a Zonge AVG file. A new AMTAVG instance will be created internally to read the file.

    • A pandas.DataFrame. A new AMTAVG instance will be created to read the DataFrame.

    • A pre-existing, loaded object that inherits from BaseAVG (e.g., an AVG or AMTAVG instance).

  • meta (mapping, optional) – An optional dictionary of metadata. This is only used when source is a pd.DataFrame.

Returns:

self – The method returns the instance of the class, allowing for convenient method chaining.

Return type:

ASTATIC

Raises:
  • TypeError – If the source is of an unsupported type.

  • NotReadError – If an AVG-like object is passed as the source but it has not been populated with data yet.

Notes

This method acts as a smart constructor for the processor. If the provided source is not already a loaded AVG object, the method takes on the responsibility of creating one. This ensures that the self.avg attribute is always a valid, ready-to-use data container.

Examples

>>> from pycsamt.zonge import AMTAVG, ASTATIC
>>> # --- Reading from a pre-loaded AVG object ---
>>> avg = AMTAVG.from_file('data/avg/K2.avg')
>>> processor = ASTATIC().read(avg)
>>>
>>> # --- Reading directly from a file path ---
>>> processor_from_file = ASTATIC().read('data/avg/K1.avg')

See also

pycsamt.zonge.avg.AVG.from_file

The primary factory for creating an AVG data object.

pycsamt.utils.validation.has_read

The underlying utility used to validate loaded data objects.

correct_capacitive_coupling(contact_resistance, setup_length, wire_capacitance=15.0, update_components=True)#

Correct for capacitive coupling effects in E-field data.

This method remediates distortions in high-frequency data caused by capacitive coupling between receiver wires and the ground.

Parameters:
  • contact_resistance (float, pd.Series, or str) – The contact resistance at the electrodes, in Ohms. Can be a single value for all measurements, a Series, or the name of a column in the dataset.

  • setup_length (float, pd.Series, or str) – The length of the setup wire, in meters. This is often the distance from the GDP to the dipole center.

  • wire_capacitance (float, pd.Series, or str, default 15.0) – The wire-to-ground capacitance in picofarads per meter (pF/m). This is often an empirical tuning parameter.

  • update_components (bool, default True) – If True, the main DataFrame (self.avg.info.df) is updated in place with the corrected E-field values, and all dependent components (Z, rho, phase) are recalculated.

Returns:

A new DataFrame containing the corrected emag and ephz columns.

Return type:

pandas.DataFrame

Notes

The correction is based on a simple circuit model where the capacitive admittance (\(Y_c\)) shunts the true Earth impedance. The measured voltage is corrected to estimate the true voltage that would be measured without this effect.

The correction factor is given by:

\[E_{true} = \frac{E_{measured}}{1 + Z_c Y_c}\]

where \(Z_c\) is the contact impedance (resistance) and \(Y_c = i \omega C\) is the capacitive admittance.

correct_static_shift(reference_freq, *, filter_method='tma', update_components=True, **kwargs)#

Correct for static shift using a spatial filter.

This method applies a spatial filter to the data at a single reference frequency to estimate and correct for static shift effects.

Parameters:
  • reference_freq (float) – The frequency (in Hz) at which to perform the spatial filtering. This should typically be the highest frequency with clean data.

  • filter_method ({'tma', 'flma', 'ama'}, default 'tma') – The filtering algorithm to use: - ‘tma’: Trimmed Moving Average (works on resistivity). - ‘flma’: Fixed-Length Moving Average (works on impedance). - ‘ama’: Adaptive Moving Average (works on impedance).

  • update_components (bool, default True) – If True, the main DataFrame (self.avg.info.df) is updated in place with the static-shifted resistivity values, and all components are re-read.

  • **kwargs – Additional keyword arguments to be passed to the chosen filtering function (e.g., window_size for tma, filter_width_dipoles for flma).

Returns:

A DataFrame containing the station, the original data profile, the smoothed profile, and the shift factor.

Return type:

pandas.DataFrame

class pycsamt.zonge.Hardware(version='7.76', source_file=None, dated=None, processed=None, astatic_ver='v3.60', updated=None, tma_points=None, tma_freq=None, _extra=<factory>)#

Bases: object

Minimal provenance captured from banner / comment lines.

Many of these are informational and may not appear as $keywords. We keep to_keywords minimal to avoid inventing conventions.

Parameters:
  • version (str)

  • source_file (Path | None)

  • dated (str | None)

  • processed (str | None)

  • astatic_ver (str)

  • updated (str | None)

  • tma_points (int | None)

  • tma_freq (float | None)

  • _extra (dict[str, Any])

version: str#
source_file: Path | None#
dated: str | None#
processed: str | None#
astatic_ver: str#
updated: str | None#
tma_points: int | None#
tma_freq: float | None#
KEYMAP: dict[str, str]#
set(**kwargs)#

Set known fields; unknowns land in _extra.

Return type:

None

get(key, default=None)#

Get known field or fallback to _extra.

Parameters:
Return type:

Any

to_json(*, indent=0)#
Parameters:

indent (int)

Return type:

str

update_from_keywords(meta)#

Accept a plain dict (keys may or may not start with ‘$’) and coerce a few well-known banner values to proper types.

Parameters:

meta (dict[str, Any])

Return type:

None

to_keywords()#

Provide a small, stable set for round-trip / tests.

Return type:

dict[str, Any]

class pycsamt.zonge.SurveyAnnotation(project_name='CSAMTSurvey', project_area=None, customer_name='Zonge Engineering', contractor_name='Zonge', project_label='pyCSAMT', acq_date=<factory>, _extra=<factory>)#

Bases: object

Project-level annotation block ($Job.*).

Parameters:
  • project_name (str)

  • project_area (str | None)

  • customer_name (str)

  • contractor_name (str)

  • project_label (str)

  • acq_date (str)

  • _extra (dict[str, Any])

project_name: str#
project_area: str | None#
customer_name: str#
contractor_name: str#
project_label: str#
acq_date: str#
KEYMAP: dict[str, str]#
ALIASES: dict[str, str]#
set(**kwargs)#

Set known fields; unknowns land in _extra.

Return type:

None

get(key, default=None)#

Get known field or fallback to _extra.

Parameters:
Return type:

Any

update_from_keywords(meta)#

Update from parsed $Job.* keys (with alias support).

Parameters:

meta (dict[str, Any])

Return type:

None

to_keywords()#

Export standardized $Job.* keys.

Return type:

dict[str, Any]

to_json(*, indent=0)#
Parameters:

indent (int)

Return type:

str

class pycsamt.zonge.SurveyConfiguration(survey_type='CSAMT', array_type=None, line_name=None, line_number=None, line_azim_deg=None, stn_gdp_beg=None, stn_gdp_inc=None, stn_beg=None, stn_inc=None, stn_left=None, stn_right=None, unit_length='m', unit_emag='nV/Am', unit_hfield='pT/A', unit_phase='mrad', utm_zone=None, created=<factory>, _extra=<factory>)#

Bases: object

Survey-level configuration taken from AVG headers.

Parameters:
  • survey_type (str)

  • array_type (str | None)

  • line_name (str | None)

  • line_number (float | None)

  • line_azim_deg (float | None)

  • stn_gdp_beg (float | None)

  • stn_gdp_inc (float | None)

  • stn_beg (float | None)

  • stn_inc (float | None)

  • stn_left (float | None)

  • stn_right (float | None)

  • unit_length (str)

  • unit_emag (str)

  • unit_hfield (str)

  • unit_phase (str)

  • utm_zone (int | None)

  • created (str)

  • _extra (dict[str, Any])

survey_type: str#
array_type: str | None#
line_name: str | None#
line_number: float | None#
line_azim_deg: float | None#
stn_gdp_beg: float | None#
stn_gdp_inc: float | None#
stn_beg: float | None#
stn_inc: float | None#
stn_left: float | None#
stn_right: float | None#
unit_length: str#
unit_emag: str#
unit_hfield: str#
unit_phase: str#
utm_zone: int | None#
created: str#
KEYMAP: dict[str, str]#
ALIASES: dict[str, str]#
set(**kwargs)#

Set known fields; unknowns are stored in _extra so nothing is lost on round-trip.

Return type:

None

get(key, default=None)#

Get known field or fallback to _extra.

Parameters:
Return type:

Any

update_from_keywords(meta)#

Populate from $… header dict (keys without ‘$’). Coerce numeric fields so tests don’t see strings.

Parameters:

meta (dict[str, Any])

Return type:

None

to_keywords()#

Export standardized $Survey.*, $Line.*, $Stn.*.

Return type:

dict[str, Any]

to_json(*, indent=0)#
Parameters:

indent (int)

Return type:

str

class pycsamt.zonge.Receiver(station=None, length_m=None, gdp_station=None, hpr=None, comps='ExHy', azimuth_deg=None, latitude=None, longitude=None, elevation=None, unit='m', notes=None)#

Bases: object

Receiver electrode / coil metadata ($Rx.*).

Parameters:
station: int | None#
length_m: float | None#
gdp_station: int | None#
hpr: tuple[float, float, float] | None#
comps: str | None#
azimuth_deg: float | None#
latitude: float | None#
longitude: float | None#
elevation: float | None#
unit: str | None#
notes: str | None#
KEYMAP: dict[str, str]#
ALIASES: dict[str, str]#
set(**kwargs)#

Set any attribute; no hard validation here.

Return type:

None

get(key, default=None)#

Get attribute by name.

Parameters:
Return type:

Any

update_from_keywords(meta)#

Update from parsed $Rx.* / $GPS.* keys with typing.

Parameters:

meta (dict[str, Any])

Return type:

None

to_keywords()#

Export standardized $Rx.* keys with units preserved.

Return type:

dict[str, Any]

class pycsamt.zonge.Transmitter(station=None, length_m=None, gdp_station=None, tx_type=None, center=None, hpr=None, current_a=None, frequency_hz=None, latitude=None, longitude=None, notes=None)#

Bases: object

Transmitter loop / bipole metadata ($Tx.*).

Parameters:
station: int | None#
length_m: float | None#
gdp_station: int | None#
tx_type: str | None#
center: tuple[float, float, float] | None#
hpr: tuple[float, float, float] | None#
current_a: float | None#
frequency_hz: float | None#
latitude: float | None#
longitude: float | None#
notes: str | None#
KEYMAP: dict[str, str]#
ALIASES: dict[str, str]#
set(**kwargs)#

Set any attribute; no hard validation here.

Return type:

None

get(key, default=None)#

Get attribute by name.

Parameters:
Return type:

Any

update_from_keywords(meta)#

Update from $Tx.* (and legacy XMTR) with typing.

Parameters:

meta (dict[str, Any])

Return type:

None

to_keywords()#

Export standardized $Tx.* keys.

Return type:

dict[str, Any]

class pycsamt.zonge.SkipFlag(value='2')#

Bases: object

Encapsulate Zonge skip-flag quality codes.

Codes#

2 → good quality 1 → kept but skip on plots 0 → rejected / bad data * → no data (placeholder)

set(value=None)#

Set the flag using {0,1,2,’*’}.

Parameters:

value (str | int | None)

Return type:

None

get()#

Return the label (‘good’, ‘skip’, …).

Return type:

str

property code: str#

Return the raw code as string.

Parameters:

value (str | int | None)

class pycsamt.zonge.Resistivity(data=None, meta=None, *, name=None, verbose=False)#

Bases: TensorBase

Apparent resistivity (\(\rho_a\)) per component.

This is a scalar variable (one number per row), but the class inherits the tensor-alignment logic so values land in the appropriate \(2\times 2\) slot given by comp.

Recognized source columns (legacy + modern)#

  • ARes.mag (modern)

  • Resistivity (legacy tables)

  • rho / Rho / ares.mag (variants)

Canonical internal column: 'rho'.

ivar VAR_NAME:

Canonical column in :pyattr:`frame` ("rho").

vartype VAR_NAME:

str

ivar ALIASES:

Ordered alias list used during read().

vartype ALIASES:

tuple[str, …]

ivar UNIT_ATTR:

Dataset attribute used for units ("Unit.Rho").

vartype UNIT_ATTR:

str

VAR_NAME: str = 'rho'#
UNIT_ATTR: str = 'Unit.Rho'#
read(source, meta=None, **kws)#

Parse source and produce a compact frame with station, freq, comp, rho. Coordinates are injected conservatively when absent (station=NaN, comp='ExHy').

Parameters:
Return type:

None

write(*, float_fmt='%.6g', na_rep='')#

Serialise to a compact CSV block with a small meta preamble (units + timestamp).

Parameters:
  • float_fmt (str)

  • na_rep (str)

Return type:

Sequence[str]

to_xarray(*, coords=('station', 'freq', 'comp'), attrs=None)#

Convert to an xarray.Dataset with one data variable (rho). Attributes include a stable unit hint under Unit.Rho.

Parameters:
Parameters:
class pycsamt.zonge.Phase(data=None, meta=None, *, name=None, verbose=False)#

Bases: TensorBase

Impedance phase (\(\varphi\)) per component.

This class manages the impedance phase data, inheriting from TensorBase to allow its scalar values to be aligned into a \(2 \times 2\) tensor-like grid based on the component label.

Notes

Values are typically reported in milliradians in modern CSAVGW tables (Unit.Phase='mrad'). Use the convert_unit() method to switch to degrees when desired.

The internal canonical column name for phase data is phase.

Recognized source columns:

  • Z.phz (modern)

  • Phase (legacy)

  • phase, ZPHZ (common variants)

Variables:
  • VAR_NAME (str) – The canonical column name used in the internal frame ("phase").

  • UNIT_ATTR (str) – The dataset attribute key used for storing units ("Unit.Phase").

Parameters:

Examples

>>> from pycsamt.zonge import Phase
>>> import pandas as pd
>>> data = {"freq": [1024], "phase": [1000]}
>>> p = Phase()
>>> p.read(pd.DataFrame(data))
>>> p.frame['phase'].iloc[0]
1000.0
>>> p.convert_unit("deg")
>>> p.frame['phase'].iloc[0]
57.2957...

See also

Resistivity

Manages apparent resistivity data.

TensorBase

The base class providing

VAR_NAME: str = 'phase'#
UNIT_ATTR: str = 'Unit.Phase'#
read(source, meta=None, **kws)#

Parse source and produce a compact frame with station, freq, comp, phase. Coordinates are injected conservatively when absent (station=NaN, comp='ExHy'). Unit.Phase defaults to 'mrad'.

Parameters:
Return type:

None

write(*, float_fmt='%.6g', na_rep='')#

Serialise to a compact CSV block with a meta preamble carrying Unit.Phase and a UTC timestamp.

Parameters:
  • float_fmt (str)

  • na_rep (str)

Return type:

Sequence[str]

to_xarray(*, coords=('station', 'freq', 'comp'), attrs=None)#

Convert to an xarray.Dataset with one data variable (phase). Attributes include a stable unit hint under Unit.Phase.

Parameters:
convert_unit(target='mrad')#

Convert the phase values in place between \(\mathrm{mrad}\) and \(\mathrm{deg}\) and update Unit.Phase in :pyattr:`meta`.

\[1~\mathrm{mrad} = \frac{180}{\pi \cdot 1000}~\mathrm{deg}\]
Parameters:

target (str)

Return type:

None

pycsamt.zonge.get_aliases(canonical_name, *, kind='all', custom_aliases=None, normalize=True)#

Fetch all known aliases for a canonical column name.

Parameters:
  • canonical_name (str) – The internal, standardized name for the column.

  • kind ({'qc', 'all'}, default 'all') –

    • ‘qc’: Search only within quality control aliases.

    • ’all’: Search within all known aliases.

  • custom_aliases (dict, optional) – A dictionary to temporarily add or override aliases. Keys are canonical names, values are tuples of aliases.

  • normalize (bool, default True) – If True, the canonical_name and keys in the custom_aliases dictionary are treated as case-insensitive.

Returns:

A tuple of all raw (legacy and modern) names that map to the given canonical name.

Return type:

tuple[str, …]

Examples

>>> get_aliases('pc_emag', kind='qc')
('%Emag', 'E.%err')
>>> get_aliases('rho')
('ARes.mag', 'Resistivity')
>>> get_aliases(
...     'rho',
...     custom_aliases={'rho': ('resistivity_ohm_m',)}
... )
('ARes.mag', 'Resistivity', 'resistivity_ohm_m')
class pycsamt.zonge.Station(unit='m', normalize=False, allow_ragged=True, values=<factory>, names=<factory>, index_by_value=<factory>, index_by_name=<factory>)#

Bases: AVGComponentBase

One-dimensional survey-line geometry container.

This component reads the station coordinate from a tidy AVG frame (usually column 'station'), validates and stores unique positions, and (optionally) normalizes the origin to zero. It also derives a stable set of header keys ($Stn.*) for round-tripping modern headers.

Key features#

  • Accepts either a DataFrame (from AVG parsers) or raw arrays.

  • Handles ragged frequency coverage (no equal-count requirement).

  • Tracks units; internal helpers can work in metres when desired.

  • Derives $Stn.Beg / $Stn.Inc / $Stn.Left / $Stn.Right if grid-like.

  • Exposes dictionary mappings useful for downstream selection.

Notes

We do not modify the caller’s AVG frame; we keep our own view in self._frame that at minimum contains a numeric 'station' column and (if relevant) a 'station_m' column when the input length unit is feet (‘ft’) and conversion to metres is helpful.

unit: str#
normalize: bool#
allow_ragged: bool#
values: ndarray#
names: list[str]#
index_by_value: dict[float, ndarray]#
index_by_name: dict[str, ndarray]#
read(source, meta=None, *, unit=None, names=None, normalize=None, allow_ragged=None)#

Populate the component from source.

Parameters:
  • source (DataFrame | Sequence[float] | ndarray) – DataFrame with a 'station' column or a 1-D array of station values (possibly repeated across frequencies).

  • meta (Mapping[str, Any] | None) – Optional file metadata. If provided and unit is None, we try meta['Unit.Length'] for (‘m’|’ft’).

  • unit (str | None) – Length unit of source values. Defaults to ‘m’ or to meta['Unit.Length'] when available.

  • names (Sequence[str] | None) – Optional labels for unique station positions. If omitted, we auto-generate: ‘S00’, ‘S01’, …

  • normalize (bool | None) – When True, shift the origin so the first station is 0.0.

  • allow_ragged (bool | None) – When False, all stations must have identical row counts (one per frequency). When True (default), ragged coverage is allowed.

Return type:

None

write()#

Emit a small, human-friendly header preamble plus a CSV of the station coordinate. This is mainly useful for diagnostics and tests; full file writing should be orchestrated at a higher level.

Return type:

Sequence[str]

property n_unique: int#

Number of unique station positions.

property span: tuple[float, float] | None#

(min, max) of the station coordinate; None if empty.

property increment: float | None#

Grid increment if evenly spaced, else None.

label_map()#

Map station numeric value → generated label (e.g., ‘S03’).

Return type:

dict[float, str]

to_keywords()#

Derive a stable set of $Stn.* keys from current geometry:

  • Stn.Beg : first station

  • Stn.Inc : increment (when grid-like)

  • Stn.Left : left edge (min)

  • Stn.Right : right edge (max)

For convenience, we also mirror GDP-station versions when increment is grid-like: Stn.GdpBeg / Stn.GdpInc.

Return type:

dict[str, Any]

Parameters:
class pycsamt.zonge.TensorBase(data=None, meta=None, *, name=None, verbose=0)#

Bases: AVGComponentBase

Adds impedance-like tensor helpers to a component.

This class acts as a mixin, providing methods to transform data between a tidy DataFrame format (one measurement per row) and a dense, multi-dimensional tensor format suitable for numerical computations.

It is agnostic to the actual physical quantity being reshaped; the var parameter in its methods specifies which column from the internal DataFrame to use for the tensor’s values.

Notes

Subclasses must provide a tidy _frame attribute containing at least the columns ['freq', 'comp'] and optionally 'station'.

The tensor axes are consistently ordered: - 3D (single station): (frequency, E-field, H-field) - 4D (multi-station): (station, frequency, E, H)

The E-field and H-field axes are of size 2, corresponding to the x and y components.

Parameters:
to_tensor(var, station=None, ...)#

Converts a data column into a NumPy ndarray with a shape of (..., 2, 2).

Parameters:
Return type:

tuple[ndarray, ndarray, ndarray]

from_tensor(tensor, freqs, var, stations=None, ...)#

Reconstructs a tidy DataFrame from a NumPy tensor.

Parameters:
Return type:

DataFrame

to_xarray_tensor(var, station=None, ...)#

Converts a data column into a labeled xarray.DataArray.

Parameters:

See also

Z

A key subclass that uses these tensor operations.

Resistivity

Another subclass that benefits from this mixin.

Phase

A third subclass that uses this mixin.

to_tensor(*, var, station=None, agg='mean', fill_value=nan, sort_freq=True, align='union')#

Convert per-component values into a 2×2 tensor per frequency.

Returns:

  • tensor (np.ndarray) – If station is provided → shape (n_freq, 2, 2). Else (multi-station) → shape (n_station, n_freq, 2, 2).

  • freqs (np.ndarray) – Sorted unique frequencies used in the tensor grid.

  • stations (np.ndarray) – Stations used (size 0 for single-station request).

Parameters:
Return type:

tuple[ndarray, ndarray, ndarray]

static from_tensor(tensor, freqs, *, var, stations=None, comp_style='mt')#

Reconstruct a tidy frame from a (…×2×2) tensor.

Parameters:
  • tensor (ndarray) – Either (n_freq, 2, 2) or (n_station, n_freq, 2, 2).

  • freqs (Sequence[float]) – Frequencies corresponding to axis 0 (or 1).

  • var (str) – Column name to emit for the tensor values.

  • stations (Sequence[int | float | str] | None) – If provided and tensor is 4-D, labels for station axis.

  • comp_style (str) – ‘mt’ → Zxx/Zxy/Zyx/Zyy ; ‘csamt’ → ExHx/ExHy/EyHx/EyHy

Return type:

DataFrame

to_xarray_tensor(*, var, station=None, agg='mean', fill_value=nan, attrs=None)#
Return a 3-D or 4-D xarray.DataArray with dims:

single-station → (freq, e, h) multi-station → (station, freq, e, h)

Parameters:
read(source, meta=None, **kws)#

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

write()#

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]

class pycsamt.zonge.Tipper(data=None, meta=None, *, name=None, verbose=False)#

Bases: AVGComponentBase

Geomagnetic transfer function (Tipper) component.

The Tipper, also known as the induction vector, is a transfer function that relates the vertical component of the magnetic field (\(H_z\)) to the horizontal components (\(H_x\), \(H_y\)).

\[H_z = T_x H_x + T_y H_y\]

This class is designed to hold the complex Tipper components \(T_x\) and \(T_y\) after they have been calculated.

Variables:

ty (tx,) – The complex Tipper components for the x and y directions.

Parameters:

See also

pycsamt.zonge.avg.AMTAVG.calculate_tipper

The method used to compute the Tipper values.

read(source, meta=None, **kws)#

Populate the Tipper component from a DataFrame.

This method expects a DataFrame that contains the calculated Tipper components, named ‘tx’ and ‘ty’. If these columns are missing, they will be created and filled with NaNs for structural consistency.

Parameters:
Return type:

None

write()#

Serialise to a compact CSV block with a meta preamble.

Return type:

list[str]

property tx: Series#

The complex Tipper component Tx.

property ty: Series#

The complex Tipper component Ty.

to_xarray(*, coords=('station', 'freq'), attrs=None)#

Convert the Tipper data into an xarray.Dataset.

Parameters:
pycsamt.zonge.load_avg(path, *, ll_columns=('latitude', 'longitude'), utm_zone=None, inplace=False)#

Read a Zonge AVG file and return a tidy DataFrame and metadata.

This function serves as the primary parser for both legacy (kind-1) and modern (kind-2) Zonge AVG files. It automatically detects the file format, parses the data accordingly, and standardizes all column names to a consistent, canonical schema.

Parameters:
  • path (str or pathlib.Path) – The filesystem path to the .avg file.

  • ll_columns (tuple[str, str], default ('latitude', 'longitude')) – Column names in the source data that contain latitude and longitude values in decimal degrees. This is not a standard Zonge field but is supported for custom data formats.

  • utm_zone (int, optional) – The UTM zone number to use for coordinate conversion. If None, the zone is auto-detected from the longitude.

  • inplace (bool, default False) – This parameter is deprecated and no longer has an effect, as the function always returns a new DataFrame.

Returns:

  • df (pandas.DataFrame) – A tidy DataFrame where all column names have been standardized to the internal canonical schema (e.g., ‘Resistivity’ becomes ‘rho’).

  • meta (dict) – A dictionary containing all header metadata from the file. For modern files, this includes a ‘blocks’ key with a list of per-station metadata blocks.

Raises:
  • FileNotFoundError – If the file specified by path does not exist.

  • AvgFileError – If the file format cannot be reliably classified as either legacy or modern.

  • AvgDataError – If parsing fails due to malformed or missing data within the file.

Return type:

tuple[DataFrame, dict[str, str]]

Notes

The standardization of column names is a key feature of this function. It ensures that all subsequent processing steps can rely on a consistent and predictable data structure, regardless of the input file’s original format. This is achieved by using the _CANONICAL_MAP from the schema module.

Examples

>>> from pycsamt.zonge.utils import load_avg
>>> # Load a modern AVG file
>>> df, meta = load_avg('data/avg/K2.avg')
>>> print(df.columns)
Index(['z_mwgt', 'freq', ..., 'station', 'comp', 'use'], dtype='object')
>>> print(meta['Survey.Type'])
CSAMT

See also

pycsamt.zonge.avg.AVG.from_file

The recommended high-level entry point for loading AVG data.

pycsamt.zonge.utils.write_avg

The corresponding function for writing AVG files.

pycsamt.zonge.write_avg(core, extra, meta, path=None, *, stamp=True, float_fmt='%.6g', na_rep='*', header_spaces=False, banner_lines=None)#

Serialize a DataFrame to a Zonge kind-2 AVG file.

This function serves as the core writer for creating modern, CSAVGW/ASTATIC-style .avg files. It takes a DataFrame with canonical column names, aggregates metadata, and formats the output into a structured text file with data blocks grouped by station.

Parameters:
  • core (pandas.DataFrame) – The main DataFrame containing the core measurement data. It is expected to have canonical column names (e.g., ‘rho’, ‘phase’, ‘pc_emag’).

  • extra (pandas.DataFrame or None) – An optional DataFrame containing additional columns to be merged with the core data before writing.

  • meta (dict, optional) – A dictionary of global metadata to be written as $keyword=value pairs in the file header.

  • path (str or pathlib.Path, optional) – The output file path. If None, a default filename like exported_kind2.avg is created in the current working directory.

  • stamp (bool, default True) – If True, a $Written=<timestamp> line is added to the header for provenance.

  • float_fmt (str, default "%.6g") – The format specifier for writing floating-point numbers in the data blocks.

  • na_rep (str, default "*") – The string representation for missing (NaN) values in the data blocks, conforming to the Zonge convention.

  • header_spaces (bool, default False) – If True, adds spaces around the equals sign in header keywords (e.g., $Key = Value).

  • banner_lines (sequence of str, optional) – A sequence of comment lines (starting with ‘\’) to be prepended to the file header, typically for hardware and processing information.

Returns:

The absolute path to the newly created .avg file.

Return type:

pathlib.Path

Notes

This function is designed to produce clean, compliant, and human-readable kind-2 AVG files. Its key behaviors include:

  • Column Renaming: It uses the CANON_TO_MODERN_MAP from the schema module to automatically convert the internal canonical column names back to the standard modern format (e.g., ‘rho’ becomes ‘ARes.mag’).

  • Block Grouping: If a ‘station’ column is present, the function intelligently groups the data by station and writes a separate data block for each, preceded by its specific $Rx.* metadata.

  • Smart Column Filtering: It automatically detects and omits placeholder columns (like ‘Choer’, ‘Gdp.Blk’) if they contain no valid data, preventing empty columns from cluttering the output file. It also excludes internal helper columns from the final output.

  • Aligned Formatting: The function uses a custom CSV formatter to produce neatly aligned, fixed-width-like columns within the data blocks, matching the appearance of files generated by Zonge’s proprietary software.

See also

pycsamt.zonge.avg.BaseAVG.to_modern

The primary method that calls this function.

pycsamt.zonge.utils.load_avg

The corresponding function for reading AVG files.

pycsamt.zonge.tma(rho_profile: str, *, data: DataFrame, window_size: int = 5, trim_proportion: float = 0.2) DataFrame#
pycsamt.zonge.tma(rho_profile: ndarray, *, data: None = None, window_size: int = 5, trim_proportion: float = 0.2) ndarray
pycsamt.zonge.tma(rho_profile: Series, *, data: None = None, window_size: int = 5, trim_proportion: float = 0.2) Series

Apply a Trimmed Moving Average (TMA) filter.

This filter is designed to remove single-station static offsets while preserving broader geological trends. It can operate on a pandas Series, a NumPy array, or a column within a pandas DataFrame.

Parameters:
  • rho_profile (pd.Series, np.ndarray, or str) –

    The apparent resistivity data to be filtered. This can be: - A pandas Series of resistivity values. - A 1D NumPy array of resistivity values. - The string name of the column to use if data is

    provided.

  • data (pd.DataFrame, optional) – A DataFrame containing the resistivity data. Required if rho_profile is provided as a string.

  • window_size (int, default 5) – The size of the moving window for the filter. Must be an odd integer.

  • trim_proportion (float, default 0.2) – The proportion of observations to trim from each end of the window before computing the mean. For a window of 5, 0.2 trims 1 value from each end (the min and max).

Returns:

The smoothed resistivity profile, returned in the same format as the input (rho_profile). If a DataFrame was passed to data, the function returns a new DataFrame with an added column for the smoothed data.

Return type:

pd.Series, np.ndarray, or pd.DataFrame

Raises:

ValueError – If rho_profile is a string but data is not provided, or if window_size is not an odd integer.

pycsamt.zonge.flma(z_profile: str, stations: str, dipole_length: float, *, data: DataFrame, filter_width_dipoles: float = 5.0) DataFrame#
pycsamt.zonge.flma(z_profile: ndarray, stations: ndarray, dipole_length: float, *, data: None = None, filter_width_dipoles: float = 5.0) ndarray
pycsamt.zonge.flma(z_profile: Series, stations: Series, dipole_length: float, *, data: None = None, filter_width_dipoles: float = 5.0) Series

Apply a Fixed-Length Moving Average (FLMA) filter.

This filter smooths complex impedance data using a spatial Hanning window whose width is a fixed multiple of the receiver dipole length.

Parameters:
  • z_profile (pd.Series, np.ndarray, or str) – The complex impedance data (Z) to be filtered. Can be: - A pandas Series of complex impedance values. - A 1D NumPy array of complex impedance values. - The string name of the column if data is provided.

  • stations (pd.Series, np.ndarray, or str) – The station locations (coordinates). Must correspond to the z_profile data. Can be a Series, array, or column name if data is provided.

  • dipole_length (float) – The length of the E-field receiver dipole in the same units as the station locations.

  • data (pd.DataFrame, optional) – A DataFrame containing the impedance and station data. Required if z_profile or stations are strings.

  • filter_width_dipoles (float, default 5.0) – The total width of the Hanning window, expressed in multiples of the dipole_length.

Returns:

The smoothed complex impedance profile, returned in the same format as the input. If a DataFrame was passed to data, a new DataFrame with an added column for the smoothed data is returned.

Return type:

pd.Series, np.ndarray, or pd.DataFrame

pycsamt.zonge.ama(z_profile: str, stations: str, dipole_length: float, frequency: float, *, data: DataFrame, skin_depth_factor: float = 2.0, iterations: int = 3) DataFrame#
pycsamt.zonge.ama(z_profile: ndarray, stations: ndarray, dipole_length: float, frequency: float, *, data: None = None, skin_depth_factor: float = 2.0, iterations: int = 3) ndarray
pycsamt.zonge.ama(z_profile: Series, stations: Series, dipole_length: float, frequency: float, *, data: None = None, skin_depth_factor: float = 2.0, iterations: int = 3) Series

Apply an Adaptive Moving Average (AMA) filter.

This filter smooths complex impedance data using a spatial Hanning window whose width is adapted iteratively based on the local skin depth.

Parameters:
  • z_profile (pd.Series, np.ndarray, or str) – The complex impedance data (Z) to be filtered.

  • stations (pd.Series, np.ndarray, or str) – The station locations (coordinates).

  • dipole_length (float) – The length of the E-field receiver dipole.

  • frequency (float) – The frequency of the data in Hz.

  • data (pd.DataFrame, optional) – A DataFrame containing the impedance and station data. Required if z_profile or stations are strings.

  • skin_depth_factor (float, default 2.0) – The filter width, expressed as a multiple of the local skin depth.

  • iterations (int, default 3) – The number of iterations to perform to allow the filter width to adapt to the smoothed data.

Returns:

The smoothed complex impedance profile, returned in the same format as the input.

Return type:

pd.Series, np.ndarray, or pd.DataFrame

pycsamt.zonge.interpolate_to_log_space(df, *, freq_min=None, freq_max=None, num_points=50, interp_kind='cubic')#

Interpolate AVG data onto a regular log-spaced grid.

This function resamples the apparent resistivity and phase data for each station and component onto a new, logarithmically spaced frequency axis. This is a common preprocessing step for inversion and modeling.

Parameters:
  • df (pandas.DataFrame) – The input DataFrame containing the AVG data. Must include ‘station’, ‘freq’, ‘rho’, and ‘phase’ columns.

  • freq_min (float, optional) – The minimum frequency for the new grid. If None, it is inferred from the minimum frequency in the data.

  • freq_max (float, optional) – The maximum frequency for the new grid. If None, it is inferred from the maximum frequency in the data.

  • num_points (int, default 50) – The number of logarithmically spaced points to create between freq_min and freq_max.

  • interp_kind (str, default 'cubic') – The kind of interpolation to perform, passed to scipy.interpolate.interp1d. Common options are ‘slinear’, ‘quadratic’, and ‘cubic’.

Returns:

A new DataFrame with the data interpolated onto the specified logarithmic frequency grid.

Return type:

pandas.DataFrame

pycsamt.zonge.smooth_rho_from_phase(df, *, smoothing_factor=0.1)#

Smooth apparent resistivity using the Hilbert transform.

This function reconstructs a smoother apparent resistivity curve from the impedance phase data, leveraging the causal nature of magnetotelluric data.

Parameters:
  • df (pandas.DataFrame) – The input DataFrame. Must include ‘station’, ‘freq’, ‘rho’, and ‘phase’ columns.

  • smoothing_factor (float, default 0.1) – The smoothing factor for the UnivariateSpline applied to the phase data before the Hilbert transform. A smaller value results in less smoothing.

Returns:

A new DataFrame with a ‘rho_smoothed’ column containing the reconstructed, smoother apparent resistivity values.

Return type:

pandas.DataFrame

pycsamt.zonge.get_reference_frequency(df, mode='auto', *, qc_column='pc_rho', qc_threshold=20.0)#

Determine a suitable reference frequency for static shift.

This utility automatically selects a reference frequency based on the principle of using the “highest frequency with clean data,” as recommended in the ASTATIC manual.

Parameters:
  • df (pandas.DataFrame) – The input DataFrame containing the AVG data. Must include ‘station’, ‘freq’, and a quality control column.

  • mode ({'auto'} or float, default 'auto') –

    The method for determining the reference frequency. - ‘auto’: Automatically select the frequency based on QC

    metrics.

    • float: A specific frequency value to use, which will be returned directly.

  • qc_column (str, default 'pc_rho') – The canonical name of the quality control column to use for filtering “clean” data points.

  • qc_threshold (float, default 20.0) – The threshold for the qc_column. Data points where the value in this column is below the threshold are considered clean.

Returns:

The determined reference frequency in Hz.

Return type:

float

Notes

The ‘auto’ mode follows a robust, data-driven approach: 1. It filters the dataset to include only “clean” data points

(where qc_column < qc_threshold).

  1. For each station, it finds the highest frequency that has clean data.

  2. It then returns the median of these highest frequencies. The median is used to provide a stable estimate that is robust against outlier stations.

If no data points meet the clean criteria, the function will issue a warning and fall back to using the absolute maximum frequency present in the entire dataset.

pycsamt.zonge.get_skew(df)#

Calculate Swift’s skew for the impedance tensor.

Skew is a dimensionless parameter that quantifies the three-dimensionality of the subsurface conductivity structure. It is a crucial diagnostic tool for determining whether a 1D or 2D interpretation is appropriate for the data.

Parameters:

df (pandas.DataFrame) – A tidy DataFrame containing the impedance tensor components. Must include ‘station’, ‘freq’, ‘comp’, and a complex ‘z’ column.

Returns:

A DataFrame with columns for ‘station’, ‘freq’, and the calculated ‘skew’.

Return type:

pandas.DataFrame

Notes

The function calculates the conventional skew definition proposed by Swift [1]_. The formula is:

\[\text{skew} = \frac{|Z_{xx} + Z_{yy}|}{|Z_{xy} - Z_{yx}|}\]

Generally, skew values are interpreted as follows: - skew < 0.1: Data can be considered 1D. - 0.1 <= skew <= 0.3: Data is likely 2D. - skew > 0.3: Data is likely 3D or affected by significant

local distortion.

Examples

>>> from pycsamt.zonge import AMTAVG
>>> from pycsamt.zonge.proc_utils import get_skew
>>> avg = AMTAVG.from_file('data/avg/K2.avg')
>>> skew_df = get_skew(avg.z.frame)
>>> print(skew_df.head())

References

See also

get_strike

Calculate the geoelectric strike angle.

pycsamt.zonge.get_strike(df)#

Calculate the geoelectric strike angle.

This function determines the principal axis direction of the impedance tensor for each station and frequency. The strike is the angle to which the coordinate system must be rotated to minimize the diagonal components of the impedance tensor (\(Z_{xx}\), \(Z_{yy}\)), which is a common assumption for 2D geological structures.

Parameters:

df (pandas.DataFrame) – A tidy DataFrame containing the impedance tensor components. Must include ‘station’, ‘freq’, ‘comp’, and a complex ‘z’ column from which the tensor elements can be pivoted.

Returns:

A DataFrame with columns for ‘station’, ‘freq’, and the calculated ‘strike_angle’ in degrees clockwise from North.

Return type:

pandas.DataFrame

Notes

The calculation is performed using a common tensor decomposition method based on the real parts of the impedance tensor components [1]_. The formula used is:

\[\theta_s = \frac{1}{2} \arctan\left( \frac{\text{Re}(Z_{xy} + Z_{yx})} {\text{Re}(Z_{xx} - Z_{yy})} \right)\]

This angle represents the orientation of the primary 2D geological structure.

Examples

>>> from pycsamt.zonge import AMTAVG
>>> from pycsamt.zonge.proc_utils import get_strike
>>> avg = AMTAVG.from_file('data/avg/K2.avg')
>>> # The Z component's frame already has the required columns
>>> strike_df = get_strike(avg.z.frame)
>>> print(strike_df.head())

References

See also

get_skew

Calculate the dimensionality indicator (skew).

pycsamt.zonge.avg.AMTAVG.rotate

Rotate the tensor by a given angle.

Zonge Modules#

pycsamt.zonge.avg

AVG - Main user-facing data container for Zonge datasets.

pycsamt.zonge.base

pycsamt.zonge.base

pycsamt.zonge.components

pycsamt.zonge.components

pycsamt.zonge.config

This module provides foundational base classes and configuration objects for the Zonge subpackage.

pycsamt.zonge.heads

Header facade that aggregates survey-level property containers (Hardware, Annotation, Configuration, Tx, Rx, Skip) and exposes a uniform read/write API based on $keyword=value lines.

pycsamt.zonge.info

DataInfo - High-level AVG data aggregator.

pycsamt.zonge.meas

pycsamt.zonge.meas

pycsamt.zonge.ops

This module contains functions for Zonge engineering calculations, based on the GDP DATA PROCESSING MANUAL.

pycsamt.zonge.processing

pycsamt.zonge.processing

pycsamt.zonge.proc_utils

pycsamt.zonge.proc_utils

pycsamt.zonge.property

pycsamt.zonge.property

pycsamt.zonge.qc

A convenient aggregator for all Quality-Control (QC) components.

pycsamt.zonge.resphase

Scalar apparent-field estimates: Resistivity and Phase.

pycsamt.zonge.schema

pycsamt.zonge.schema

pycsamt.zonge.survey

Survey layout.

pycsamt.zonge.tensor

TensorBase – generic 2×2 impedance-like tensor adapter.

pycsamt.zonge.tipper

Tipper Transfer Function Component.

pycsamt.zonge.utils

General‑purpose helpers for Zonge AVG / AMTAVG files and accompanying station profiles.

pycsamt.zonge.var_pc

Percent-variation (QC) components for Zonge AVG tables.

pycsamt.zonge.var_std

Phase-variation quality metrics:

pycsamt.zonge.z

Impedance tensor (Z) component for Zonge AVG data.