2.9. pycsamt.seg#
SEG-style survey parsing, EDI objects, measurement sections, spectra, time-series helpers, and validation tools.
EDI section models for metadata, spectra, time series, and impedance data.
- class pycsamt.seg.EDIMixin#
Bases:
CoreObjectLightweight registry and helpers used by EDI readers.
The mixin stores parsed section objects (e.g.
>HEAD,>=MTSECT,>=SPECTRASECT) in a simple dictionary, and provides tiny utilities to manage and query them.It does not perform I/O. Host classes remain free to decide when and how sections are discovered and populated. This keeps the orchestration logic small and testable.
- Variables:
sections (dict[str, object]) – Case–insensitive mapping from a logical section key (e.g.
"head","mtsect","spectra") to the parsed object that represents that section.
- add_section(key, obj)#
Register
objunderkey. Keys are normalized to lower case.
- get_section(key)#
Retrieve a previously added section or
None.
- has_section(key)#
Return
Trueif a section exists underkey.
- _tag2name(tag)#
Translate a raw
>=...header tag to a canonical registry key (e.g.">=MTSECT" → "mtsect").
Notes
The registry is intentionally untyped so that different parser implementations can coexist. For instance, a project may store a header object (
SpectraSECT) and also the decoded payload object (Spectra) under separate keys.Examples
>>> mix = EDIMixin() >>> mix._init_registry() >>> mix.add_section("HEAD", object()) >>> mix.has_section("head") True >>> isinstance(mix.get_section("head"), object) True
See also
EDIFileHigh level reader that uses the registry to expose parsed sections to callers.
References
[EDIMixin-1]SEG EDI MT/EMAP standard (1987). MTNet.
- class pycsamt.seg.EDIOMixin#
Bases:
CoreObjectTolerant
>BLOCKparser and TF (Z/Tipper) builder.The mixin provides two core utilities used by
EDIFileafter headers are discovered:_scan_blocks()reads numeric blocks starting at a given line (e.g.>FREQ,>ZXXR,>TXR.EXP)._build_from_comp()converts the scanned dictionary intopycsamt.z.z.Zandpycsamt.z.tipper.Tipperobjects.
The reader accepts both complex tensor blocks and the scalar families (
RHO*andPHS*). When complex blocks are missing, the impedance tensor is reconstructed from resistivity and phase if possible.- Variables:
None
- _scan_blocks(path, start=None, empty_val=1e32)#
Return a mapping
key → list[float]by streaming lines until the next section or EOF. Unknown keys are ignored. Values equal toempty_val(the EDI missing- data sentinel) are converted to NaN, never to zero – zero is a valid measured value and must not be invented for a period the instrument never recorded.
- _build_from_comp(comp, z_obj, tip_obj)#
Populate
z_objandtip_objfrom the components. Frequency order is normalized to descending. Z–error arrays are converted from variance blocks (.VAR).
Notes
Frequency order is unified to high→low. This follows a common practice in EDI archives and simplifies plotting.
The tipper is optional. If no tipper blocks are found,
tip_objis left untouched.For
RHO*/PHS*the method also carries*.ERRif present. Otherwise zero errors are assumed.
Examples
>>> comp = {"freq": [10, 1], "zxxr": [1, 2], "zxxi": [0, 0]} >>> from pycsamt.z.z import Z >>> from pycsamt.z.tipper import Tipper >>> mix = EDIOMixin() >>> z, t = Z(), Tipper() >>> mix._build_from_comp(comp, z_obj=z, tip_obj=t) >>> z.n_freq 2
See also
EDIFileUses these utilities during
read_data().
References
[EDIOMixin-1]SEG EDI MT/EMAP standard (1987). MTNet.
- class pycsamt.seg.EDIFile(path=None, *, verbose=0)#
-
High–level EDI dispatcher for SEG/EMAP/CSAMT archives.
The class discovers top–level headers (e.g.
>=MTSECT,>=SPECTRASECT,>=TSERIESSECT), loads the matching data blocks, and exposes convenient Python containers for impedance tensors, tippers, spectra, and time series.It also writes EDI files by reusing the in–memory sections, preserving headers when possible, and regenerating block payloads from the current objects.
- Parameters:
- Variables:
path (Path or None) – Bound file path (if any).
Z (pycsamt.z.z.Z) – Impedance tensor container with errors and rotations.
Tip (pycsamt.z.tipper.Tipper) – Tipper container (optional).
sections (dict[str, object]) – Registry populated via
EDIMixin.block_size (int) – Numbers per line when writing numeric payloads.
float_fmt (str) – Float formatter used for numeric blocks.
header_tpl (str) – Template used for logical block titles.
Properties
----------
station (str or None) – Shortcut to
>HEAD.DATAID. Setter also mirrors the value to the MT/EMAP section id.processingsoftware (str or None) – Shortcut to the name of the processing software from
>INFO.
- read(path=None)#
Load headers and sections. Parse numeric blocks into
ZandTip. Also attachesSpectraandTimeSeriesif present.
- compose_headers()#
Serialize only the headers (no data blocks).
- write(...)#
Write a full EDI assembled from current objects and sections. MT or EMAP numeric families are chosen from the MT/EMAP header or inferred from context.
- interpolate(new_freq, kind="slinear", ...)#
Interpolate
Zon a new frequency grid. The grid is rounded to two decimals for stable serialization.
- write_new_edi(edi_fn=None, Z=None, Tipper=None, ...)#
Rebuild a clean container bound to the same source, swap selected transfer functions, and delegate to
write().
Notes
Frequency order is normalized to descending on read. Therefore, a file written by
write()and read back will exposeZ.freqin high→low order.The interpolation routine enforces the new grid to live strictly inside the source span when
bounds_errorisTrue.Missing blocks are handled gracefully. If complex impedances are absent, the reader tries to reconstruct them from
RHO*/PHS*families.
Examples
>>> ed = EDIFile("site.edi") >>> ed.station 'SITE' >>> ed.Z.n_freq > 0 True >>> out = ed.write(savepath="outdir") >>> Path(out).exists() True >>> fnew = np.geomspace(ed.Z.freq.min() * 1.1, ed.Z.freq.max() * 0.9, 16) >>> z2 = ed.interpolate(fnew, kind="linear") >>> ed.write_new_edi(edi_fn="interp.edi", Z=z2)
See also
EDIMixinRegistry and convenience helpers used internally.
EDIOMixinNumeric block reader and TF builder used by
read_data().
pycsamt.seg.spectra.Spectra,pycsamt.seg.time_series.TimeSeriesReferences
[EDIFile-1]SEG EDI MT/EMAP standard (1987). MTNet.
[EDIFile-2]B. Groom, R. Bailey (1989). Decomposition of the magnetotelluric impedance tensor. Geophysics.
- compose_headers(*, stamp_head=True)#
Serialize structural EDI headers without numeric data blocks.
stamp_headpreserves the historical default in which>HEADfile/program timestamps are refreshed byHead. Format converters may disable stamping when they need to retain mapped provenance metadata exactly.
- write(edi_fn=None, new_edifn=None, datatype=None, savepath=None, add_filter_array=None, synthesize_spectra=False, preserve_zero=False, stamp_headers=True, force_tipper=None, **kwargs)#
- interpolate(new_freq, *, kind='slinear', bounds_error=True, period_buffer=None)#
- interpolate_z(new_freq, *, kind='slinear', bounds_error=True, period_buffer=None)#
- write_new_edi(edi_fn=None, Z=None, Tipper=None, *, Spectra=None, TimeSeries=None, sections=None, **kwargs)#
- property spectra_sect#
- property timeseries_sect#
- property spectra#
Return high-level Spectra object if present.
- property spectra_io#
Return SpectraIO if present.
- property timeseries#
Return high-level TimeSeries object if present.
- property timeseries_io#
Return TSIO if present.
- class pycsamt.seg.Spectra(name=None, *, verbose=0)#
Bases:
EMBaseContainer for
>SPECTRAblocks grouped per frequency.The class gathers one spectra record per frequency and exposes typed header fields (frequency, rotation flag, bandwidth, and averaging time) together with the numeric values stored in each block. It is a compact, array- oriented view on top of
SpectraSECTandSpectraIO.- Parameters:
- Variables:
freq (ndarray, shape
(n_blk,)) – Frequency (Hz) per block. Missing values are set tonp.nan.rotspec (ndarray of int, shape
(n_blk,)) – Rotation specifier per block. Missing values are set to-1.bw (ndarray, shape
(n_blk,)) – Nominal bandwidth (Hz) per block ornp.nan.avgt (ndarray, shape
(n_blk,)) – Averaging time (s) per block ornp.nan.values (list of ndarray) – Numeric payload for each block. Lengths may differ across blocks, as allowed by the SEG format.
n_values (ndarray of int, shape
(n_blk,)) – Number of values in each block (as parsed or counted).
Notes
Blocks may contain vendor-specific options beyond the canonical
FREQ,ROTSPEC,BW, andAVGT. Those options are preserved when round-tripping viato_io(). The class does not impose a common length across spectra vectors; if you require a 2-D array, pad thevalueslist explicitly.The constructor itself does not read files. Use
from_io()orfrom_file()to populate an instance from sections and data blocks.To recover a format-neutral EMTF document with full FCU-compatible covariance from this container, use
pycsamt.emtf.EMTF.from_edi_spectra()orpycsamt.emtf.converters.spectra.spectra_to_emtf()on thepycsamt.emtfside rather than a method on this class — spectra parsing stays inpycsamt.seg, transfer-function/covariance recovery stays in the EMTF interoperability layer.- from_io(sect, io) : classmethod
Build a
SpectrafromSpectraSECTandSpectraIO.
- from_file(path) : classmethod
Convenience that calls
SpectraSECT.from_fileandSpectraIO.from_file, then delegates tofrom_io().
- to_io()#
Serialize the current state to a fresh pair (
SpectraSECT,SpectraIO) that can be written back to an EDI file.- Return type:
Examples
Read, inspect, and serialize spectra:
from pycsamt.seg.spectra import Spectra sp = Spectra.from_file("site.edi") f = sp.freq first = sp.values[0] sect2, io2 = sp.to_io() # writer can now combine sect2.write() and io2.write()
See also
pycsamt.seg.spectra.SpectraSECTHeader for
>=SPECTRASECTsections.pycsamt.seg.spectra.SpectraIOReader/writer for
>SPECTRAblocks.pycsamt.seg.EDIFileHigh-level dispatcher that can attach spectra to an EDI session.
References
[Spectra-1]SEG EDI standard, Spectra Data Sections. Society of Exploration Geophysicists.
[Spectra-2]Chave, A. D., & Jones, A. G. (2012). The Magnetotelluric Method: Theory and Practice. Cambridge Univ. Press.
- property fcu_cross_spectra: ndarray#
Return spectra in the EMTF-FCU cross-power convention.
Spectra.Sretains the historical pyCSAMT conventionchannel_i * conj(channel_j)so existing callers andto_Z()are unchanged by Phase 8. EMTF FCU uses the conjugate conventionconj(channel_i) * channel_j.EDI SPECTRA input keeps an exact FCU view in
_S_fcu. For spectra constructed through the historical pyCSAMT API, the FCU view is therefore the element-wise complex conjugate ofS.
- property missing_mask: ndarray | None#
Return the per-frequency missing cross-spectral component mask.
- classmethod from_io(sect, io, *, empty=1e+32, verbose=0)#
- Parameters:
sect (SpectraSECT)
io (SpectraIO)
empty (float)
verbose (int)
- Return type:
- classmethod from_file(path, *, empty=1e+32, verbose=0)#
Read a
Spectradirectly from an EDI file path.Convenience wrapper around
from_io()that callsSpectraSECT.from_fileandSpectraIO.from_fileinternally.
- to_edi(source_edi=None, *, station_name=None, e_labels=('EX', 'EY'), h_labels=('HX', 'HY'), ridge=None, estimate_error=False, dof=None)#
Convert cross-spectra to an MT-impedance
EDIFile.Calls
to_Z()and assembles a complete>=MTSECT/>FREQ/>ZXXR/>ZXYR/ … EDI ready to be saved withwrite().The structural sections (
>HEAD,>INFO,>=DEFINEMEAS) are re-used from source_edi when provided, preserving all acquisition metadata; otherwise a minimal header is synthesised from theSpectrametadata.According to the SEG EDI standard (§ 7.53, 12.1), an MT data section requires:
>=MTSECT SECTID=... NFREQ=... HX=... HY=... HZ=... EX=... EY=... >FREQ //N ... >ZXXR ROT=ZROT //N ... >ZXXI ROT=ZROT //N ... ... >END
The measurement IDs for
HX,HY, … in>=MTSECTare resolved fromid_to_chtype(populated bySpectraSECTfrom>HMEAS/>EMEASlines).- Parameters:
source_edi (str, Path, or EDIFile, optional) – Spectra EDI file whose
>HEAD,>INFO, and>=DEFINEMEASsections are copied into the output. Pass the same path used withfrom_file()to produce a fully metadata-rich result. WhenNone, a minimal header is synthesised.station_name (str, optional) – Override for the
DATAIDin>HEADandSECTIDin>=MTSECT. Defaults toname.e_labels (tuple of str) – Electric channel type labels forwarded to
to_Z().h_labels (tuple of str) – Horizontal magnetic channel type labels forwarded to
to_Z().ridge (float, optional) – Tikhonov regularisation forwarded to
to_Z().estimate_error (bool) – If
True, propagate 1-σ errors into>ZXX.VAR… blocks.dof (float or ndarray, optional) – Effective degrees of freedom forwarded to
to_Z().
- Returns:
Fully populated MT-impedance container. Call
write()to save.- Return type:
- Raises:
EdIDataError – If
to_Z()fails (channel types not resolved, singular magnetic block, etc.).
Examples
Convert and save:
sp = Spectra.from_file("site.edi") ed = sp.to_edi("site.edi", estimate_error=False) out = ed.write(savepath="mt_output/")
Convert with a custom station name and error propagation:
ed = sp.to_edi( "site.edi", station_name="HBH03_imp", estimate_error=True, dof=24.0, ) out = ed.write(savepath="mt_output/")
Verify the round-trip:
from pycsamt.seg.edi import EDIFile ed2 = EDIFile(out) assert ed2.Z.n_freq == sp.n_freq
- to_io()#
- Return type:
- validate_frequency_count(*, policy='raise')#
Validate
SPECTRASECT.NFREQagainst parsed/usable blocks.- Parameters:
policy ({"raise", "warn", "ignore"}) – Action when the declared count differs from either the number of parsed
>SPECTRAblocks or the number of usable frequency blocks. FCU historically stops on this inconsistency;warnis provided for recovery of heterogeneous archives.- Returns:
Trueif the declaredNFREQmatches both the parsed and usable block counts (or noNFREQwas declared);Falsewhen a mismatch is found andpolicyis"warn"or"ignore".- Return type:
- Raises:
ValueError – If
policyis not one of"raise","warn","ignore".EdIDataError – If a mismatch is found and
policy="raise".
- rotate(theta_deg, *, pairs=None)#
- to_Z(*, id_to_chtype=None, e_labels=('EX', 'EY'), h_labels=('HX', 'HY'), use_remote=False, ridge=None, estimate_error=True, dof=None)#
Recover an impedance tensor
Zand, if available, the tipper from cross-spectra stored in thisSpectra.The method resolves channel types, extracts the electric and magnetic sub-blocks, and computes per-frequency
Z = S_EH @ inv(S_HH). If a vertical magnetic channel is present it also computes the tipperT = S_ZH @ inv(S_HH). Optional ridge regularization can be applied to stabilize the magnetic block.- Parameters:
id_to_chtype (dict of str to str, optional) – Mapping from measurement IDs (as recorded in
>=SPECTRASECTorDefineMeas) to channel types ("HX","HY","HZ","EX","EY"). If omitted, the method usesself.id_to_chtypewhen available, otherwise it interpretsself.chan_idsdirectly as labels.e_labels (tuple of str, default (
"EX","EY")) – Labels that identify the two electric channels used for theEblock.h_labels (tuple of str, default (
"HX","HY")) – Labels that identify the two horizontal magnetic channels used for theHblock.use_remote (bool, default
False) – When duplicate electric channels exist (e.g., local and remote), choose the second occurrence for theEblock ifTrue; otherwise choose the first.ridge (float, optional) – Non-negative Tikhonov regularization added to
S_HHprior to inversion,S_HH + ridge * I.estimate_error (bool, default
True) – IfTrue, estimate per-component 1-sigma standard errors forZ(and tipper when available) usingcompute_errors_from_Sand the degrees of freedom given bydof(or inferred; see Notes).dof (float or ndarray, optional) – Effective degrees of freedom per frequency. If an array is provided it must broadcast to
n_freq. IfNoneandestimate_errorisTrue, the method tries to infer DoF from metadata viaeffective_dof_from_metausingsegnum, oravgt * bwas a fallback.
- Returns:
z_obj (
pycsamt.z.z.Z) – Impedance object on the spectra frequency grid withzpopulated and, when estimated,z_errset.tip (
pycsamt.z.tipper.TipperorNone) – Tipper on the same grid whenHZis available. When errors are estimated, tipper uncertainties are attached.
- Raises:
EdIDataError – If spectra are empty, channel types cannot be resolved, or the stabilized magnetic block is singular.
Notes
Per frequency,
Zis formed asZ = S_EH @ inv(S_HH), whereS_EHis the cross-spectra between E and H, andS_HHis the magnetic auto/cross block. If a vertical magnetic channel is available, the tipper is computed asT = S_ZH @ inv(S_HH).Channel type resolution proceeds in this order:
explicit
id_to_chtypeargument,self.id_to_chtypefrom the section header orDefineMeas,direct interpretation of
self.chan_ids.
When both local and remote electric channels are present, setting
use_remote=Truechooses the second occurrence as a simple heuristic. Frequency ordering is preserved.Uncertainties are computed by first-order propagation under a complex-Wishart model and scale as
1 / sqrt(DoF). If DoF cannot be determined for every frequency, no per-frequency array is attached at all:z_err(andtip.tipper_errwhen applicable) come back asNonerather than an array of NaN.Examples
>>> Zhat, That = spectra.to_Z() >>> Zhat, _ = spectra.to_Z(use_remote=True, ridge=1e-6) >>> Zhat, _ = spectra.to_Z(dof=np.full(spectra.n_freq, 24.0))
See also
Spectra.from_ZInverse operation that synthesizes spectra.
spectra_from_ZFunctional wrapper for the inverse operation.
effective_dof_from_metaInfer DoF from
segnum,avgtandbw.compute_errors_from_SPer-frequency uncertainty estimator.
References
[Spectra-to-Z-1]Chave, A. D., & Jones, A. G. (2012). The Magnetotelluric Method: Theory and Practice. Cambridge University Press.
[Spectra-to-Z-2]Bendat, J. S., & Piersol, A. G. (2011). Random Data: Analysis and Measurement Procedures. Wiley.
- classmethod from_Z(z_obj, **kws)#
Create a
Spectrafrom a transfer functionZ.This class method is a thin, convenience wrapper around
spectra_from_Z(). It synthesizes a full Hermitian cross–spectral density tensor from the impedance tensorZ(f)and optional inputs that control magnetic power and tipper usage.- Parameters:
z_obj (
Z) – Input impedance object. The attributesz_obj.z(shape(n, 2, 2)) andz_obj.freq(shape(n,)) must be set.**kws (Any) – Forwarded to
spectra_from_Z(). See that function for the complete set of options such asS_HH,H_psd,tipper,include_hz, andchan_order.
- Returns:
A spectra container on the same frequency grid as
z_obj. Channel order follows the requestedchan_order(default:HX, HY, EX, EY).- Return type:
- Raises:
EdIDataError – If
z_objis incomplete (missingzorfreq).
Notes
Absolute spectral levels are not carried by the impedance tensor. To obtain physically scaled spectra, provide magnetic spectra via
S_HHorH_psd. If neither is given, a unit–power assumption is used (S_HH = I), which is suitable for tests but not for quantitative analysis.This method does not infer per–frequency metadata such as bandwidth or averaging time; those fields are initialized with zeros/NaNs.
Examples
>>> ed = EDIFile("site_imp.edi") >>> sp = Spectra.from_Z( ... ed.Z, ... H_psd=(np.ones(ed.Z.n_freq), np.ones(ed.Z.n_freq), None), ... ) >>> sect, io = sp.to_io() >>> _ = ed.write_new_edi( ... edi_fn="site_with_synth_spec.edi", ... Spectra=sp, ... )
See also
spectra_from_ZFunctional API that performs the synthesis.
pycsamt.seg.ops.synthesize_spectra_from_zLow–level array helper used under the hood.
Spectra.to_ZInverse operation (spectra → Z).
References
[Spectra-from-Z-1]Chave, A. D., & Jones, A. G. (2012). The Magnetotelluric Method: Theory and Practice. Cambridge Univ. Press.
[Spectra-from-Z-2]Bendat, J. S., & Piersol, A. G. (2011). Random Data: Analysis and Measurement Procedures. Wiley.
[Spectra-from-Z-3]SEG EDI MT/EMAP standard (1987). MTNet.
- class pycsamt.seg.SpectraSECT(*args, verbose=0, logger=None, **kws)#
Bases:
EDIComponentBaseMinimal container for the
>=SPECTRASECTheader.The class parses and serializes the spectra section header that precedes one or more
>SPECTRAdata blocks. It collects the option key/values and the ordered set of measurement IDs that the spectra apply to, as described by the SEG EDI convention [SpectraSECT-1].- Parameters:
- Variables:
sectid (str or None) – Section identifier, often a site name. Some files omit this or use a numeric ID.
nchan (int or None) – Number of channels in the spectra set.
nfreq (int or None) – Number of frequencies expected in the section.
maxblks (int or None) – Maximum number of blocks. Rarely used.
meas_ids (list of str) – Ordered measurement ID list that follows the option lines in
>=SPECTRASECT.start_data_lines_num (int or None) – Line index in the EDI where the first
>SPECTRAblock begins. Set byfrom_file().
Notes
Parsing is tolerant to case and extra whitespace.
Unknown header keys are ignored instead of raising.
The measurement ID list is collected from the header body once option lines end.
The start of the spectra data is detected by the first
>SPECTRAtag, by the next>=...tag, or by end of file, whichever comes first.For consistent processing, maintain the same frequency set across related data sections, as recommended in the EDI spec [SpectraSECT-1].
See also
Examples
Read only the header and measurement IDs:
>>> sect = SpectraSECT.from_file("site.edi") >>> sect.nfreq, sect.nchan (128, 5) >>> sect.meas_ids[:2] ['HX1', 'HY1']
Serialize a header:
>>> sect.nfreq = 3 >>> sect.meas_ids = ["HX", "HY", "EX", "EY"] >>> lines = sect.write() >>> print("".join(lines).strip()) >=SPECTRASECT SECTID=... NCHAN=... NFREQ=3 MAXBLKS=... // 4 HX HY EX EY
References
- class pycsamt.seg.SpectraIO(*args, verbose=0, logger=None, **kws)#
Bases:
EDIComponentBaseRead and write
>SPECTRAdata blocks.A spectra section contains one block per frequency. Each block begins with a
>SPECTRAline that holds options such as frequency and bandwidth, optionally followed by a comment with the number of values, then one or more lines of numeric values.Known options are normalized:
FREQ: floatROTSPEC: intBW: floatAVGT: float
Unrecognized options are preserved in a free-form mapping so that vendor-specific metadata is not lost.
- Parameters:
- Variables:
blocks (list of _SpectraBlock) – Parsed spectra blocks, one per frequency. Each block stores header options, the optional value count hint, and the numeric values.
Notes
from_file()reads successive>SPECTRAblocks starting from a given line or from the first match in the file.Values are parsed as floats; non-numeric tokens in data lines are ignored rather than raising.
The writer orders known options first in the header line, then appends extra options sorted by key. Both option keys and values are written in upper case.
Line formatting uses the per-line and float format defaults from
Baseunless you provide explicit overrides.
See also
SpectraSECTHeader container for spectra sections.
TSIOTime-series counterpart for
>TSERIES.
Examples
Read all spectra blocks:
>>> io = SpectraIO.from_file("site.edi") >>> len(io.blocks) 128 >>> b0 = io.blocks[0] >>> b0.freq, b0.bw (..., ...)
Build and serialize blocks:
>>> from pycsamt.seg.spectra import _SpectraBlock >>> io = SpectraIO() >>> blk = _SpectraBlock() >>> blk.freq = 10.0 >>> blk.rotspec = 1 >>> blk.values = [0.1, 0.2, 0.3] >>> io.blocks.append(blk) >>> lines = io.write(per_line=2, float_fmt="{: .3E}") >>> print("".join(lines).strip()) >SPECTRA FREQ=10.0 ROTSPEC=1 // 3 1.000E-01 2.000E-01 3.000E-01
References
[SpectraIO-1]SEG EDI standard, “Spectra Data Sections”.
- classmethod from_file(edi_path, start_line=None)#
- exception pycsamt.seg.SpectraValidationWarning#
Bases:
UserWarningWarning emitted for recoverable EDI SPECTRA inconsistencies.
- class pycsamt.seg.TimeSeries(name=None, *, verbose=0)#
Bases:
EMBaseContainer for
>TSERIESdata aggregated by channel.The class groups samples by channel
IDand keeps a per-channel sampling interval. It is a light facade built on top ofTSectandTSIO.- Parameters:
- Variables:
ids (list of str) – Ordered channel identifiers (e.g.
["HX","HY"]).data (dict[str, ndarray]) – Mapping
channel -> 1-D samples. Each array has length equal to the concatenation of all blocks that belong to that channel, in file order.dt_map (dict[str, float]) – Mapping
channel -> dt(seconds). When a block has noDToption,TSect.dtis used as a fallback. If neither is present,1.0is used intime().npts_map (dict[str, int]) – Mapping
channel -> number of samplesaccumulated across all blocks.extra_blocks (list of dict) – Optional raw per-block metadata preserved for round- tripping or vendor-specific fields.
Notes
The class is designed for two common workflows:
Build from parsed IO. Use
from_io()with a header (TSect) and a data stream (TSIO). The constructor performs channel discovery, concatenation, anddtassignment.Write back to EDI. Use
to_io()to obtain a fresh (TSect,TSIO) pair. One block per channel is emitted, using the accumulated data and the finaldtchosen for that channel.
Channel order is stable and follows first appearance in the input stream. The
time()vector is computed asnp.arange(N) * dtfor the requested channel.- time(cid)#
Return a 1-D time vector using
dt_map[cid]or the agreed fallback.
- from_io(sect, io, empty=None) : classmethod
Build a
TimeSeriesfrom parsed header and IO.
- align(ids=None, fill=0.0)#
Right-pad channels to the same length and return a 2-D array
(nmax, nch)and the channel order.
Examples
Build from blocks and compute a time vector:
from pycsamt.seg.time_series import TSect, TSIO from pycsamt.seg.time_series import TimeSeries sect = TSect(sectid="TS", dt=0.25) io = TSIO() # filled elsewhere ts = TimeSeries.from_io(sect, io) hx = ts.get("HX") t = ts.time("HX")
Round-trip to EDI blocks:
sect2, io2 = ts.to_io() # pass sect2.write() and io2.write() to your writer
See also
pycsamt.seg.time_series.TSectHeader parser for
>=TSERIESSECT.pycsamt.seg.time_series.TSIOReader/writer for
>TSERIESblocks.
References
[TimeSeries-1]SEG EDI MT/EMAP standard (1987). MTNet. https://www.mtnet.info/docs/seg_mt_emap_1987.pdf
- classmethod from_io(sect, io, *, empty=None)#
- Parameters:
- Return type:
- class pycsamt.seg.TSect(*args, verbose=0, logger=None, **kws)#
Bases:
EDIComponentBaseMinimal container for the
>=TSERIESSECTheader block. It parses the section header and the ordered list of measurement IDs that follow the header. The class keeps a pointer to where the first>TSERIESdata block starts so downstream readers can jump straight to the data.- Parameters:
- Variables:
sectid (str or None) – Section identifier. If absent in file it remains
None.nchan (int or None) – Number of channels declared in the header.
nmeas (int or None) – Number of measurements declared in the header.
npts (int or None) – Number of samples per trace if provided.
maxblks (int or None) – Hint for the maximum number of data blocks.
dt (float or None) – Sampling interval in seconds when present.
meas_ids (list of str) – Ordered list of measurement IDs collected from the header tail. One ID per line.
extra (dict) – Any non standard key–value options preserved as strings.
start_data_lines_num (int or None) – Absolute line index where the first
>TSERIESblock begins. Useful for fast data scans.
- from_file(edi_path)#
Parse a single
>=TSERIESSECTfrom an EDI file. The method validates the file structure withvalidation.IsEdi._assert_edi()before parsing.
- write()#
Serialize the section back to EDI lines including the measurement ID list.
Notes
Parsing is tolerant. Unknown keys are stored in
extra. Blank lines and comment lines beginning with//are ignored. If multiple time-series sections exist, callfrom_file()on the desired file view or use a higher level iterator to locate the right header first.Examples
>>> sect = TSect.from_file("sound.edi") >>> sect.nchan, sect.dt (3, 0.01) >>> print("IDs:", sect.meas_ids[:2]) IDs: ['HX', 'HY']
See also
TSIOReader and writer for
>TSERIESdata blocks.validation.IsEdiLightweight EDI file validator used during reading.
References
[TSect-1]SEG EDI MT/EMAP standard (1987). MTNet. https://www.mtnet.info/docs/seg_mt_emap_1987.pdf
- class pycsamt.seg.TSIO(*args, verbose=0, logger=None, **kws)#
Bases:
EDIComponentBaseReader and writer for
>TSERIESdata blocks. Each data block line starts with a flexible option list (e.g.ID=HX NPTS=4 DT=0.25) followed by a// Nhint and then one or more lines of numeric samples.- Parameters:
- Variables:
blocks (list of _TSBlock) –
Parsed time-series blocks in file order. Every block exposes:
options: dict of parsed header options.nvals_hint: int or None from the//count.values: list[float] of samples.id: str or None (alias ofoptions['id']).npts: int or None (alias ofoptions['npts']).dt: float or None (alias ofoptions['dt']).
- from_file(edi_path, start_line=None, \*, verbose=0, logger=None)#
Parse all
>TSERIESblocks starting atstart_line. Ifstart_lineisNonethe first block is located automatically. The method assumes the file already passedvalidation.IsEdi._assert_edi()upstream.
- write(per_line=None, float_fmt=None)#
Serialize every block.
per_linecontrols how many samples are printed per line.float_fmtcontrols the numeric format (e.g."{: .6E}").
Notes
Header options are typed heuristically. Integer-like tokens become integers. Otherwise they are parsed as floats when possible, and finally left as strings. The common aliases
id,nptsanddtare mirrored onto block fields for convenience.Examples
>>> sect = TSect.from_file("sound.edi") >>> io = TSIO.from_file("sound.edi", start_line=sect.start_data_lines_num) >>> len(io.blocks) 2 >>> io.blocks[0].id, io.blocks[0].dt ('HX', 0.25) >>> lines = io.write(per_line=5, float_fmt="{: .3E}") >>> print("".join(lines).splitlines()[0]) >TSERIES ID=HX NPTS=4 DT=0.25 // 4
References
[TSIO-1]SEG EDI MT/EMAP standard (1987). MTNet. https://www.mtnet.info/docs/seg_mt_emap_1987.pdf
- classmethod from_file(edi_path, start_line=None, *, verbose=0, logger=None)#
- class pycsamt.seg.OtherSECT(*args, verbose=0, logger=None, **kws)#
Bases:
EDIComponentBaseHeader container for
>=OTHERSECTblocks.This lightweight container parses the header that opens an Other Data Section as defined by the SEG-EDI spec. It collects canonical options, any extra key/value pairs, and the ordered list of measurement IDs that may follow the options list.
- Parameters:
- Variables:
sectid (str or None) – Section identifier. Often mirrors
DATAID.nchan (int or None) – Number of channels in this section.
nfreq (int or None) – Number of frequencies, if provided.
maxblks (int or None) – Upper bound on the number of data blocks.
ndipole (int or None) – EMAP-style dipole count, if present.
type (str or None) – Free-form type tag found in the wild (e.g.
FREE).extra (dict) – Any unrecognized header keys are stored here.
meas_ids (list of str) – Measurement IDs listed under the header.
start_data_lines_num (int or None) – Absolute line index where the first
>BLOCKbegins.
Notes
Unknown header keys are preserved in
extraso round-tripping does not lose information. Measurement IDs are appended in the order they appear in the file.Examples
>>> hdr = OtherSECT.from_file("site.edi") >>> hdr.sectid 'B1' >>> hdr.meas_ids[:2] ['HX', 'HY'] >>> lines = hdr.write() >>> print("".join(lines).splitlines()[0]) >=OTHERSECT
See also
OtherIORead and write generic
>BLOCKdata.OtherMixinConvenience helpers for host classes.
References
[OtherSECT-1]SEG EDI Standard (MT/EMAP), 1987. MTNet archive.
- class pycsamt.seg.OtherIO(*args, verbose=0, logger=None, **kws)#
Bases:
EDIComponentBaseReader/writer for generic
>BLOCKdata under OTHERSECT.The class iterates over consecutive
>KEYdata blocks that follow an>=OTHERSECTheader, and stores each as a small record. Numeric rows are collected into_OtherBlock.values. Non-numeric rows are kept in_OtherBlock.raw_linesto preserve content that cannot be parsed as floats.- Parameters:
- Variables:
blocks (list of _OtherBlock) – Parsed data blocks in file order.
- from_file(path, start_line=None, verbose=0, logger=None)#
Parse all
>BLOCKentries. Ifstart_lineisNone, the reader seeks the first>OTHERor the line after>=OTHERSECT. RaisesEdIDataErrorwhen no blocks are found.
Notes
Typing strategy. Option values are parsed with a best-effort rule: integers first, then floats, else kept as strings. Numeric table rows are formatted using
FLOAT_FMTand wrapped usingPER_LINE.Examples
>>> hdr = OtherSECT.from_file("site.edi") >>> io = OtherIO.from_file("site.edi", start_line=hdr.start_data_lines_num) >>> [b.keyword for b in io.blocks] ['>COH', '>ANNO'] >>> out = io.write() >>> print(out[0].strip().split()[0]) >COH
See also
OtherSECTHeader that precedes the data blocks.
OtherMixinHelper methods for host classes.
References
[OtherIO-1]SEG EDI Standard (MT/EMAP), 1987. MTNet archive.
- classmethod from_file(edi_path, start_line=None, *, verbose=0, logger=None)#
- class pycsamt.seg.IsEdi#
Bases:
ABCAbstract base for SEG-EDI validation helpers.
Subclasses implement
IsEdi.is_valid. The static method_assert_edi()provides a robust, file-level validator that accepts either a path or an existingIsEdiinstance. The check is heuristic, fast, and tolerant of minor formatting issues.The validator recognizes three EDI families:
Impedance-style files that contain a
>FREQblock and a measurement section such as>=MTSECT,>=DEFINEMEAS,>=EMAPSECTor>=OTHERSECT.Spectra-style files that include
>=SPECTRASECTand or at least one>SPECTRAblock.Time-series files that include
>=TSERIESSECTand or at least one>TSERIESblock.
In addition, the first top-level tag must be
>HEADand the last must be>END. On failure the method raises a descriptiveEdIDataError.Notes
The check is structural rather than semantic. It does not validate numerical values or cross-block consistency. Files are read as text using
utf-8-sigwitherrors="replace"to gracefully handle odd encodings.Examples
Validate an EDI file path:
>>> from pycsamt.seg.validation import IsEdi >>> IsEdi._assert_edi("path/to/site.edi") True
Use with an object that implements
is_valid:>>> class MyEdi(IsEdi): ... @property ... def is_valid(self): ... return True >>> IsEdi._assert_edi(MyEdi()) True
See also
pycsamt.seg.mtemap.MTEMAP.from_fileParse
>=MTSECT/>=EMAPSECTheaders.pycsamt.seg.spectra.SpectraSECT.from_fileParse
>=SPECTRASECTheaders.pycsamt.seg.time_series.TSect.from_fileParse
>=TSERIESSECTheaders.
References
[IsEdi-1]SEG (1987). MT/EMAP EDI Format Standard. Society of Exploration Geophysicists. Available online: https://www.mtnet.info/docs/seg_mt_emap_1987.pdf
- abstract property is_valid: bool#
Indicate whether the current instance represents a structurally valid EDI object.
This property is used by
IsEdi._assert_edi()when a concreteIsEdiinstance is provided instead of a file path.- Returns:
Truewhen the instance is valid. Subclasses decide the exact criteria.- Return type:
Notes
Implementations should be lightweight and side-effect free. Heavy validation belongs to dedicated readers (e.g., MTEMAP, Spectra, Time-Series parsers).
- class pycsamt.seg.EDIProfile(items, *, verbose=0)#
Bases:
SurveyBaseProfile helper for one or many
EDIFileobjects. Computes small-area geometry (easting/northing), cumulative distance along line, profile azimuth, and exposes utilities to adjust coordinates and push them back into EDI headers.The class accepts a single file, an iterable of files, or an
EDICollection. Coordinates are read from>HEADand converted to working planar coordinates. For short lines the equirectangular approximation is used, and distances/azimuth are computed in that local frame.- Parameters:
- Variables:
stations (list of str) – Station identifiers resolved from
DATAIDor file name.lon (lat,) – Geographic coordinates (degrees).
elev (ndarray of float) – Elevations when present, missing values become
0.distance (ndarray of float) – Cumulative distance from the first site (meters).
azimuth (float) – Bearing of the profile in degrees, clockwise from North, in
[0, 360).xy (tuple of ndarray) – Working (easting, northing) arrays in meters.
table (list of dict) – Row-wise view exposing station, lat, lon, elev, easting, northing, and UTM zone (when available).
- get_bearing(method='endpoints')#
Compute bearing from either endpoints or a PCA-like fit of the track.
- get_step()#
Return cumulative distance and cache it for reuse.
- adjust(origin=None, azimuth=None, spacing=None, use_mean=True)#
Build an idealized straight profile and compute adjusted positions and lat/lon.
- update(use_adjusted=True, update_elev=False)#
Write back adjusted coordinates to each site’s header.
- Parameters:
- Return type:
- plot_profile(use_adjusted=False, annotate=True, title=None)#
Plot elevation against along-profile distance.
- plot_track(use_adjusted=False, title=None)#
Plot plan-view easting/northing track.
Notes
The small-area equirectangular frame is adequate for short profiles. For long lines or large latitude spans prefer a full projection workflow. When the UTM zone can be determined, adjusted coordinates are converted back to geographic using that zone.
Examples
Load two sites and compute azimuth and distance:
prof = EDIProfile([ed1, ed2]) print(float(prof.azimuth)) d = prof.distance
Adjust to a regular spacing and push back to headers:
prof.adjust(spacing=50.0).update()
See also
StationsTabular view of station metadata and projected coordinates.
TopographyElevation profile builder with smoothing and trend tools.
References
[EDIProfile-1]SEG EDI MT/EMAP standard (1987), MTNet. https://www.mtnet.info/docs/seg_mt_emap_1987.pdf
[EDIProfile-2]Snyder, J. P. (1987). Map Projections – A Working Manual, USGS Prof. Paper 1395.
- get_bearing(*, method='endpoints')#
Estimate the survey bearing (azimuth) in degrees.
- Parameters:
method ({‘endpoints’, ‘linear’}, default
'endpoints') – With'endpoints'the azimuth is computed from the first to the last station. With'linear'a best-fit axis is estimated by SVD of centered coordinates.- Returns:
Bearing in
[0, 360)(clockwise from north), orNoneif fewer than two valid stations exist.- Return type:
float or None
Notes
Uses working projected coordinates (easting/northing), suitable for small-area profiles.
- get_step(*, method='mean', as_array=False)#
Derive the inter-station spacing from the track.
- Parameters:
method ({‘mean’, ‘median’}, default
'mean') – Aggregation used when returning a scalar spacing.as_array (bool, default
False) – IfTrue, return the pairwise segment lengths as a 1-D array of sizen-1. IfFalse, return a single spacing computed withmethod.
- Returns:
Either a scalar spacing or the per-segment distances.
- Return type:
float or ndarray
Notes
Distances are computed from consecutive projected coordinates; missing stations are ignored.
- adjust(*, origin=None, azimuth=None, spacing=None, step=None, use_mean=True)#
Build an idealized, straightened profile and store the adjusted coordinates.
- Parameters:
origin (tuple(float, float), optional) – Reference
(easting, northing)for the first station. Defaults to the first raw station.azimuth (float, optional) – Bearing of the adjusted line in degrees. Defaults to
get_bearing().spacing (float, optional) – Fixed spacing between consecutive stations (meters).
step (float, optional) – Alias for
spacingfor convenience.use_mean (bool, default
True) – When bothspacingandstepareNone, compute spacing from observed distances using mean ifTrueor median ifFalse.
- Returns:
The instance (allows chaining).
- Return type:
Notes
Adjusted easting/northing are projected back to latitude and longitude using the dominant UTM zone of the track. Results are stored in
_adj_e/_adj_n/_adj_lat/_adj_lon.
- update(*, use_adjusted=True, update_elev=False)#
Push current coordinates back into the underlying EDI headers.
- Parameters:
use_adjusted (bool, default
True) – IfTruewrite adjusted lat/lon (fromadjust()). If no adjusted coordinates exist, fall back to raw lat/lon.update_elev (bool, default
False) – Also write elevations when present in the profile table.
- Returns:
The instance (allows chaining).
- Return type:
Notes
This mutates the in-memory
EDIFileobjects held by the profile; it does not write to disk.
- plot_profile(*, ax=None, use_adjusted=False, annotate=True, title=None)#
Plot elevation versus along-profile distance.
- Parameters:
ax (matplotlib.axes.Axes, optional) – Target axes. If omitted, a new figure/axes is made.
use_adjusted (bool, default
False) – IfTruerecompute distances from adjusted coordinates for the overlay. Raw elevation values are used in both cases.annotate (bool, default
True) – Draw station labels next to points.title (str, optional) – Axes title.
- Returns:
The axes with the profile plot.
- Return type:
Notes
Uses the profile’s cached cumulative distances. Call
adjust()first to visualize an adjusted line.
- plot_track(*, ax=None, use_adjusted=False, title=None)#
Plot plan-view station positions (easting vs. northing).
- Parameters:
ax (matplotlib.axes.Axes, optional) – Target axes. If omitted, a new figure/axes is made.
use_adjusted (bool, default
False) – Plot the straightened track if adjusted coordinates exist; otherwise plot raw positions.title (str, optional) – Axes title.
- Returns:
The axes with the track plot.
- Return type:
Notes
Axes aspect is set to equal for a faithful plan view.
- class pycsamt.seg.Stations(items, *, verbose=0)#
Bases:
SurveyBaseLightweight table view for station metadata derived from
EDIFileobjects. Provides quick access to names, geographic coordinates, optional elevations, and working projected coordinates to support survey tasks.- Parameters:
- Variables:
- table()#
Materialize the rows as a list of dicts with keys
station,lat,lon,elev,e(east),n(north),zoneandpath.
- to_dataframe()#
Convert the table to a pandas DataFrame when pandas is available.
- bounds()#
Geographic bounding box as
(min_lat, min_lon, max_lat, max_lon).
- select(keys=None, pattern=None, regex=None, pred=None)#
Return a filtered view. Multiple filters are combined with logical AND. See the method docstring for details.
- sort(by='station', reverse=False, inplace=True)#
Sort rows by a column (e.g.
'station','lat','lon','elev','e','n'). Returns this instance or a new view depending oninplace.
- offsets(origin=None, azimuth=None)#
Compute along-line and cross-line offsets in meters from projected coordinates. The profile axis is set by
azimuthor inferred from endpoints.
- set_coords(key, \*, lat=None, lon=None, elev=None)#
Update coordinates for a single station. When the backing
EDIFileis available its>HEADvalues are kept in sync.
Notes
The class performs minimal validation. Rows missing lat/lon are skipped. Projected coordinates are intended for short-range work; for mapping at scale prefer the GIS utilities provided elsewhere in the package.
Examples
Build a table and print a compact view:
sts = Stations(coll) for r in sts.table(): print(r["station"], r["lat"], r["lon"])
Filter, sort, and compute offsets:
sel = sts.select(pattern="K*", pred=lambda r: r["elev"] > 800) sel.sort(by="e") along, across = sel.offsets()
See also
EDIProfileTrack-aware helper that computes distance and azimuth.
TopographyProduces elevation profiles from stations or profiles.
References
[Stations-1]SEG EDI MT/EMAP standard (1987), MTNet. https://www.mtnet.info/docs/seg_mt_emap_1987.pdf
- select(*, keys=None, pattern=None, regex=None, pred=None)#
Return a filtered view of the stations table.
Multiple filters are combined with logical AND. When no filter is given the original view is returned.
- Parameters:
keys (sequence of str, optional) – Station identifiers to keep. Unknown ids are ignored.
pattern (str, optional) – Glob-like pattern matched against station ids (e.g.
'AB*'). Case-sensitive.regex (str, optional) – Regular expression matched against station ids using
re.search().pred (callable, optional) – A predicate
pred(row) -> boolevaluated on each row dict. Keep rows for which the predicate returnsTrue.
- Returns:
A new
Stationsview with rows that match the filters.- Return type:
Notes
Filtering does not modify the original container. Rows lacking a station id are always dropped.
Examples
Keep stations starting with
'K'and above 800 m:sel = sts.select(pattern="K*", pred=lambda r: r["elev"] > 800)
- sort(*, by='station', reverse=False, inplace=True)#
Sort the stations table by a column.
- Parameters:
by (str, default
'station') – Column name to sort by (e.g.'station','lat','lon','elev','e','n').reverse (bool, default
False) – IfTruesort in descending order.inplace (bool, default
True) – IfTruemodify this instance and return it. Otherwise return a new sorted view.
- Returns:
The sorted
Stationsobject (self or a copy).- Return type:
Notes
Missing values are placed at the end. Unknown columns raise a
KeyError.
- offsets(*, origin=None, azimuth=None)#
Compute along-line and cross-line offsets (meters).
Offsets are computed from projected coordinates. The along-line axis is defined by the given
azimuth; the cross-line axis is perpendicular to it.- Parameters:
- Returns:
along (ndarray of float) – Distances projected on the profile axis.
across (ndarray of float) – Signed distances perpendicular to the profile axis.
- Return type:
Notes
Rows without valid projected coordinates are skipped in the computation and do not contribute to the result.
- set_coords(key, *, lat=None, lon=None, elev=None)#
Update coordinates for a single station.
- Parameters:
- Return type:
None
Notes
The in-memory row is updated. If the backing
EDIFileis attached for that station, its>HEADvalues are also updated to keep them in sync. Unknown station ids raise aKeyError.
- to_dataframe(*, columns=None, index='station', coerce_numeric=True)#
Return a pandas
DataFrameview of the station table.- Parameters:
columns (sequence of str, optional) – Subset and ordering of columns to include. When omitted, a sensible default is used:
('station','lat','lon','elev','e','n','zone', 'path'). Missing names are ignored.index (str or None, default
'station') – Column to set as the DataFrame index. If the name is not present, no index is set. UseNoneto leave the default RangeIndex.coerce_numeric (bool, default
True) – Try converting known numeric columns (lat,lon,elev,e,n) to numeric dtypes. Non convertible values becomeNaN.
- Returns:
A DataFrame with one row per station.
- Return type:
Notes
pandasis imported lazily. If it is not available, anImportErroris raised. The method is read-only and does not mutate the underlying table.Examples
Basic usage:
df = Stations(coll).to_dataframe() print(df.head())
Custom subset and index:
df = Stations(coll).to_dataframe( columns=("station", "elev", "e", "n"), index="station", )
- class pycsamt.seg.Topography(items, *, use_profile_step=True, verbose=0)#
Bases:
SurveyBaseElevation profile helper. Builds paired arrays of distance and elevation from an
EDIProfile,Stations, a collection, or rawEDIFileinputs. Includes smoothing, detrending, resampling, and quick plotting.- Parameters:
items (EDIProfile or Stations or EDICollection or EDIFile ) – or iterable of EDIFile Source of station positions and elevations. When an
EDIProfileis given, along-line distances are reused by default.use_profile_step (bool, default
True) – IfTrueanditemsis anEDIProfile, copy its along-profile distances; otherwise recompute distances from planar coordinates.verbose (int, default
0) – Verbosity level for diagnostics.
- Variables:
- smooth(window=5, method='median')#
Apply moving median or mean smoothing to elevation.
- Parameters:
- Return type:
- detrend()#
Remove a best-fit linear trend and keep it for plotting.
- Return type:
- resample(step)#
Resample to a fixed distance step using interpolation.
- Parameters:
step (float)
- Return type:
- gradient(as_degrees=False)#
First derivative of elevation vs distance; optionally in degrees.
- plot(ax=None, title=None, show_trend=True)#
Quick plot of elevation vs distance.
Notes
If the input is an
EDIProfilethat has not yet computed distances, they are derived automatically. When distances or elevations are missing the result is empty.Examples
From a profile and detrend before plotting:
topo = Topography(prof).detrend().smooth(window=7) ax = topo.plot(title="Detrended topography")
From a list of files, resampled every 25 m:
topo = Topography(edis).resample(step=25.0)
See also
EDIProfileProvides distances and azimuth and can adjust station positions.
StationsTabular access to station metadata.
References
[Topography-1]SEG EDI MT/EMAP standard (1987), MTNet. https://www.mtnet.info/docs/seg_mt_emap_1987.pdf
- smooth(*, window=5, method='median')#
Smooth the elevation series with a sliding window.
- Parameters:
window (int, default
5) – Window length (samples). Values<=1skip smoothing.method ({‘median’, ‘mean’}, default
'median') – Smoothing kernel.'median'is robust to spikes;'mean'uses a simple moving average.
- Returns:
The instance (in place), allowing chaining.
- Return type:
Notes
Edge handling is performed by shrinking the window near the bounds. This modifies the internal elevation array.
- detrend()#
- Return type:
- resample(*, step)#
Resample distance/elevation to a fixed along-line step.
- Parameters:
step (float) – Target spacing in meters for the resampled profile.
- Returns:
The instance (in place), allowing chaining.
- Return type:
Notes
Distances are regridded on
[dmin, dmax]with uniform spacing and elevations are linearly interpolated.
- gradient(*, as_degrees=False)#
Compute local slope between consecutive samples.
- Parameters:
as_degrees (bool, default
False) – IfTrue, return the slope angle in degrees. IfFalse, return the rise-over-run ratio.- Returns:
Array of length
len(distance) - 1with per-segment slopes (or angles when requested).- Return type:
ndarray
- plot(*, ax=None, title=None, show_trend=True)#
Plot elevation versus distance.
- Parameters:
ax (matplotlib.axes.Axes, optional) – Target axes. A new figure/axes is created when omitted.
title (str, optional) – Title for the axes.
show_trend (bool, default
True) – Overlay the last computed trend line (fromdetrend()) when available.
- Returns:
The axes with the rendered profile.
- Return type:
- class pycsamt.seg.XAMixin#
Bases:
objectA mixin that adds convenient xarray exports to collection classes.
This mixin provides a bridge between collection-like objects (such as
EDICollection) and the powerful, multi-dimensional data structures offered by the xarray library. Any class that is iterable overEDIFileinstances can inherit from this mixin to gain methods for data conversion and metadata extraction.- to_xarray(drop_empty=True)#
Converts the entire collection into a single, comprehensive
xarray.Dataset. This method leveragesbuild_dataset()to handle the conversion and concatenation of multiple EDI files.- Parameters:
drop_empty (bool)
- Return type:
Dataset
- meta_table()#
Extracts only the site-level metadata (e.g., coordinates, filenames, data quality flags) from the collection and returns it as a clean, tabular
xarray.Dataset, omitting the bulky transfer function data.- Return type:
Dataset
Notes
This mixin is designed to be lightweight and does not impose any specific storage or indexing strategy on the host class; it only requires that the host class implements the __iter__ method to yield
EDIFileobjects.The site identifiers used in the resulting datasets follow the same robust inference rules as
build_dataset().
See also
build_datasetThe core function that performs the conversion.
EDICollectionA primary user of this mixin.
EDIAccThe accessor for interacting with the created dataset.
Examples
To use this mixin, simply inherit from it in your collection class.
>>> from pycsamt.seg.edi import EDIFile >>> class MyEDICollection(XAMixin): ... def __init__(self, items): ... self._items = list(items) ... ... def __iter__(self): ... return iter(self._items) >>> # Assume "site1.edi" and "site2.edi" exist >>> edi_files = [ ... EDIFile("data/edis/S01.edi"), ... EDIFile("data/edis/S02.edi"), ... ] >>> collection = MyEDICollection(edi_files) >>> >>> # Convert the entire collection to an xarray Dataset >>> ds = collection.to_xarray() >>> print(ds.site.values) ['S01' 'S02'] >>> >>> # Get a summary table of just the metadata >>> metadata_ds = collection.meta_table() >>> print(metadata_ds[["lat", "lon"]]) <xarray.Dataset> Dimensions: (site: 2) Coordinates: * site (site) object 'S01' 'S02' Data variables: lat (site) float64 26.05 26.05 lon (site) float64 -10.33 -10.33
- meta_table()#
Extracts site-level metadata into a new Dataset.
- Return type:
Dataset
- pycsamt.seg.build_dataset(edis, *, drop_empty=True)#
Build a multi-site xarray Dataset from an iterable of EDIFile.
This function iterates through a collection of parsed
EDIFileobjects, converts each one into a single-sitexarray.Dataset, and then concatenates them into a unified, multi-site dataset.- Parameters:
- Returns:
A single dataset containing data from all valid EDI files. The dataset is indexed by a
sitedimension, and site-specific metadata (latitude, longitude, etc.) are stored as non-dimensional coordinates aligned with this dimension.- Return type:
xr.Dataset
Notes
The resulting dataset is structured with dimensions for sites, frequencies, and tensor components. This structure is ideal for vectorized computations and advanced plotting across multiple sites.
This function correctly handles site-specific metadata by assigning it to coordinates, preventing data loss during concatenation, which is a common pitfall when storing metadata in global attributes.
See also
Examples
>>> from pycsamt.seg import EDICollection, build_dataset >>> # Create a collection of EDI files >>> edi_collection = EDICollection.from_sources("data/edis/") >>> # Build the xarray dataset >>> ds = build_dataset(edi_collection) >>> print(ds) <xarray.Dataset> Dimensions: (site: 2, freq: 60, ...) Coordinates: * site (site) object 'S01' 'S02' * freq (freq) float64 320.0 286.9 ... ... lat (site) float64 26.05 26.05 lon (site) float64 -10.33 -10.33 Data variables: z (site, freq, output_ch, input_ch) complex128 ... z_err (site, freq, output_ch, input_ch) float64 ... ...
- class pycsamt.seg.EDIAcc(ds)#
Bases:
objectAn xarray accessor for convenient interaction with EDI datasets.
This accessor is registered under the
.edinamespace and provides domain-specific methods and properties for datasets created bybuild_dataset(). It simplifies common data selection and visualization tasks that are specific to MT/EM (magnetotelluric/electromagnetic) data.2.9. Properties#
- stationslist[str]
A list of all unique station or site names present in the dataset’s
sitecoordinate.
- get(site)#
Selects and returns a new
xarray.Datasetcontaining data for only a single site, specified by its name. The selection is case-insensitive.- Parameters:
site (str)
- Return type:
Dataset
- band(fmin=None, fmax=None)#
Filters the dataset to a specific frequency range. Returns a new dataset containing only the data within the inclusive frequency bounds.
- plot_apparent_resistivity(site, \*\*kwargs)#
Generates a standard plot of apparent resistivity and phase curves for the off-diagonal tensor components (XY and YX) of a specified site.
See also
build_datasetThe function used to create datasets compatible with this accessor.
Examples
>>> from pycsamt.seg import EDICollection, build_dataset >>> edi_collection = EDICollection.from_sources("data/edis/") >>> ds = build_dataset(edi_collection) >>> >>> # Get a list of all station names >>> print(ds.edi.stations) ['S01', 'S02', 'S03', ...] >>> >>> # Select data for a single station (case-insensitive) >>> site_data = ds.edi.get("s01") >>> >>> # Filter the data to a specific frequency band (e.g., 1 to 100 Hz) >>> filtered_ds = ds.edi.band(fmin=1.0, fmax=100.0) >>> >>> # Create a standard plot for a site >>> fig, axes = ds.edi.plot_apparent_resistivity(site="S01") >>> # fig.show() # Uncomment to display plot
- get(site)#
Selects data for a single site (case-insensitive).
- Parameters:
site (str)
- Return type:
Dataset
- plot_apparent_resistivity(site, components=None, phase_mod=None, figsize=(8, 8), show_grid=True, grid_props=None, savefig=None, **plot_kwargs)#
Generates a standard plot of apparent resistivity and phase.
This method provides a flexible interface for visualizing MT (magnetotelluric) data, allowing customization of components, phase wrapping, and plot aesthetics.
- Parameters:
site (str) – The site identifier to plot.
components (list of str, default=["xy", "yx"]) – A list of tensor components to plot (e.g., “xy”, “yx”, “xx”). The selection is case-insensitive.
phase_mod (int, optional) – If provided, wraps the phase to a specific quadrant. For example,
phase_mod=90will display phases in the [0, 90] degree range, useful for visualizing data in a single quadrant.figsize (tuple[int, int], default=(8, 6)) – The figure size for the plot.
show_grid (bool, default=True) – Whether to display a grid on both subplots.
grid_props (dict, optional) – Additional properties to customize the grid lines (e.g.,
{'color': 'grey', 'linestyle': '--', 'linewidth': 0.5}).savefig (str, optional) – If a path is provided, the plot will be saved to that file.
**plot_kwargs – Additional keyword arguments passed directly to xarray’s
.plot.line()method for customizing the lines.
- Returns:
fig (matplotlib.figure.Figure) – The matplotlib Figure object.
axes (np.ndarray of matplotlib.axes.Axes) – An array containing the two subplot Axes objects.
Examples
>>> # Basic plot of off-diagonal components >>> fig, axes = ds.edi.plot_apparent_resistivity(site="S01") >>> # fig.show()
>>> # Plot all components and save the figure >>> fig, axes = ds.edi.plot_apparent_resistivity( ... site="S01", ... components=["xy", "yx", "xx", "yy"], ... savefig="S01_all_components.png", ... )
>>> # Plot with phase wrapped to the first quadrant and custom styling >>> fig, axes = ds.edi.plot_apparent_resistivity( ... site="S01", ... phase_mod=90, ... grid_props={"color": "red", "linestyle": ":"}, ... marker="o", # passed to plot.line ... )
- band(fmin=None, fmax=None)#
- spectra()#
- Return type:
Dataset
- timeseries()#
- Return type:
Dataset
- Parameters:
ds (xr.Dataset)
2.9.1. SEG Modules#
|
Base classes for SEG-EDI objects and components. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SEG-EDI metadata. |
|
SEG-EDI schema (keywords, options, ordering). |
|
|
|
|
|
|
|
|
|
SEG-EDI helpers: minimal parsing and serialization utilities. |
|
SEG-EDI validators (no base-class deps). |
|