Quality-Control Confidence Scoring#

pycsamt.emtools.qc turns transfer-function quality control into tables and figures. It does two related jobs:

  • summarize station-level data quality, coverage, SNR, tipper presence, and phase-tensor skew;

  • compute confidence ratios from several finite, bounded scores so that stations and individual station-frequency samples can be ranked, plotted, masked, or down-weighted before inversion.

Full callable signatures live in the API reference. This page explains the confidence-ratio formulation, the table workflow, the plotting workflow, and how confidence values should be read in practice.

Why QC Is More Than Coverage#

A station can be complete and still be questionable. frac_ok=1.0 only says that impedance rows are finite. It does not say that the off-diagonal modes agree, that diagonal leakage is small, that phase is smooth, that uncertainty is low, or that the station is coherent with its neighbours.

The QC module therefore separates three ideas:

coverage

Are the required transfer-function rows finite?

confidence

How trustworthy is a station or a station-frequency cell after combining coverage, uncertainty, tensor-shape, phase, and spatial criteria?

flags

Which simple thresholds does a station or frequency cell fail?

Load A Survey#

All QC functions accept the usual pyCSAMT inputs, but a reproducible script should normalize once with ensure_sites.

 1from pathlib import Path
 2
 3from pycsamt.emtools import ensure_sites
 4
 5survey = ensure_sites(
 6    Path("data/AMT/WILLY_DATA/L18PLT"),
 7    recursive=True,
 8    on_dup="replace",
 9    strict=True,
10    verbose=1,
11)

Use strict=True for reports and automated processing. Use strict=False in exploratory notebooks if you want empty plots to render as “no data” messages.

The Confidence Ratio#

The composite confidence ratio is a weighted mean of available component scores:

\[\mathrm{CR}_{i,f} = { \sum_k w_k s_{k,i,f} \mathbf{1}_{s_{k,i,f}\ \mathrm{finite}} \over \sum_k w_k \mathbf{1}_{s_{k,i,f}\ \mathrm{finite}} }, \qquad 0 \le s_k \le 1.\]

Missing scores are ignored. Finite scores are clipped to [0, 1]. The default weights are:

Score

Weight

Meaning

coverage

0.35

Finite impedance rows or components.

uncertainty

0.20

Median relative impedance error.

offdiag

0.15

Similarity of Zxy and Zyx amplitudes.

diagonal

0.10

Penalty for diagonal leakage into off-diagonal response.

phase

0.10

Penalty for abrupt off-diagonal phase jumps.

spatial

0.10

Coherence with neighbouring stations.

The default confidence bands are:

  • CR >= 0.95: safe / retained;

  • 0.85 <= CR < 0.95: recoverable or marginal;

  • CR < 0.85: reject, down-weight, or manually review.

Compute A Confidence Ratio Directly#

Use confidence_ratio when you already have scores and want to apply the same weighted formula used by the tables.

 1from pycsamt.emtools.qc import confidence_ratio
 2
 3scores = {
 4    "coverage": 1.00,
 5    "uncertainty": 0.82,
 6    "offdiag": 0.76,
 7    "diagonal": 0.55,
 8    "phase": 0.90,
 9    "spatial": 0.88,
10}
11
12cr, cr_err = confidence_ratio(
13    scores,
14    n_freq=53,
15    return_error=True,
16)
17
18print(f"CR={cr:.3f} +/- {cr_err:.3f}")
CR=0.861 +/- 0.141

confidence_err is the spread of the available component scores. If only one score is available, it falls back to sqrt(CR * (1 - CR) / n_freq).

Station QC Summary#

build_qc_table is the first station-level table. It reports the number of frequencies, finite-row coverage, tipper availability, median row SNR when z_err exists, period range, and optional phase-tensor skew.

 1from pycsamt.emtools import build_qc_table, ensure_sites
 2
 3survey = ensure_sites("data/AMT/WILLY_DATA/L18PLT", strict=True)
 4
 5qc = build_qc_table(
 6    survey,
 7    include_skew=True,
 8    api=False,
 9)
10
11print(
12    qc[
13        [
14            "station",
15            "n_freq",
16            "n_ok",
17            "frac_ok",
18            "n_tip",
19            "snr_med",
20            "pmin",
21            "pmax",
22            "skew_med",
23        ]
24    ].head()
25)
   station  n_freq  n_ok  frac_ok  ...    snr_med      pmin      pmax   skew_med
0  18-001A      53    53      1.0  ...  17.658396  0.000096  0.992063  50.326802
1  18-002U      53    53      1.0  ...  16.687366  0.000096  0.992063  36.059416
2  18-003A      53    53      1.0  ...  12.031672  0.000096  0.992063  31.245824
3  18-004A      53    53      1.0  ...  10.430580  0.000096  0.992063  31.005169
4  18-005U      53    53      1.0  ...  14.360341  0.000096  0.992063  36.404849

[5 rows x 9 columns]

Read frac_ok as completeness, not as full confidence. Read snr_med as a row-level signal-to-error ratio when impedance error tensors are available. Read skew_med as structural/tensor complexity, not automatically as bad acquisition.

Station Flags#

qc_flags adds simple threshold labels to the station QC table.

 1from pycsamt.emtools import qc_flags
 2
 3flags = qc_flags(
 4    "data/AMT/WILLY_DATA/L18PLT",
 5    min_frac_ok=0.60,
 6    min_snr_med=2.0,
 7    max_skew_med=6.0,
 8)
 9
10flagged = flags[flags["flags"] != ""]
11print(flagged[["station", "frac_ok", "snr_med", "skew_med", "flags"]])
    station  frac_ok    snr_med   skew_med      flags
0   18-001A      1.0  17.658396  50.326802  high_skew
1   18-002U      1.0  16.687366  36.059416  high_skew
2   18-003A      1.0  12.031672  31.245824  high_skew
3   18-004A      1.0  10.430580  31.005169  high_skew
4   18-005U      1.0  14.360341  36.404849  high_skew
5   18-006A      1.0  13.272516  30.359830  high_skew
6   18-007U      1.0  14.556106  34.174772  high_skew
7   18-008U      1.0  15.890594  40.862593  high_skew
8   18-009A      1.0  14.084767  25.288856  high_skew
9   18-010U      1.0  13.677553  26.006304  high_skew
10  18-011A      1.0  12.575896  27.534040  high_skew
11  18-012A      1.0  12.873938  32.307319  high_skew
12  18-013U      1.0  11.944265  29.705384  high_skew
13  18-014A      1.0  14.847554  35.458520  high_skew
14  18-015U      1.0  13.456819  22.459269  high_skew
15  18-016A      1.0  14.746954  23.525350  high_skew
16  18-017U      1.0  13.951389  22.912833  high_skew
17  18-018A      1.0  19.352988  66.547818  high_skew
18  18-019U      1.0  14.483638  61.945234  high_skew
19  18-020A      1.0  18.506868  45.332864  high_skew
20  18-021B      1.0   9.339868  52.388774  high_skew
21  18-021U      1.0  14.119590  55.017266  high_skew
22  18-022U      1.0   8.557456  65.349813  high_skew
23  18-022V      1.0  11.268011  66.787861  high_skew
24  18-023A      1.0   9.854384  67.022970  high_skew
25  18-023V      1.0  12.137127  59.306877  high_skew
26  18-024U      1.0   8.987776  63.853268  high_skew
27  18-025A      1.0  11.770850  55.354566  high_skew

Possible station flags include low_coverage, low_snr, and high_skew. A high-skew flag can be a real structural signal. It does not mean the station should automatically be deleted.

Presence Confidence Versus Composite Confidence#

station_confidence_table has two modes. method="presence" is coverage-only. method="composite" combines all available component scores.

 1from pycsamt.emtools import station_confidence_table
 2
 3presence = station_confidence_table(
 4    "data/AMT/WILLY_DATA/L18PLT",
 5    method="presence",
 6    api=False,
 7)
 8composite = station_confidence_table(
 9    "data/AMT/WILLY_DATA/L18PLT",
10    method="composite",
11    api=False,
12)
13
14print("presence range")
15print(presence["confidence"].min(), presence["confidence"].max())
16
17print("composite range")
18print(composite["confidence"].min(), composite["confidence"].max())
19
20ranked = composite.sort_values("confidence")
21print(ranked[["station", "confidence", "coverage", "uncertainty",
22              "offdiag", "diagonal", "phase", "spatial"]].head())
presence range
1.0 1.0
composite range
0.5440199944767435 0.8119223880398303
    station  confidence  coverage  ...  diagonal     phase   spatial
22  18-022U    0.544020       1.0  ...  0.000000  0.941638  0.000000
21  18-021U    0.570986       1.0  ...  0.000000  0.938165  0.000000
17  18-018A    0.578410       1.0  ...  0.086979  0.957546  0.000000
16  18-017U    0.595479       1.0  ...  0.261699  0.969783  0.038160
18  18-019U    0.615292       1.0  ...  0.000000  0.959348  0.541804

[5 rows x 8 columns]

If presence confidence is high everywhere but composite confidence varies, the survey is complete but not equally trustworthy everywhere. That is common in real EM data.

Customize Confidence Weights#

Use custom weights when a project has a clear processing policy. For example, inversion preparation may emphasize uncertainty and coverage, while structural interpretation may care more about off-diagonal and spatial coherence.

 1from pycsamt.emtools import station_confidence_table
 2
 3weights = {
 4    "coverage": 0.40,
 5    "uncertainty": 0.30,
 6    "offdiag": 0.10,
 7    "diagonal": 0.05,
 8    "phase": 0.05,
 9    "spatial": 0.10,
10}
11
12table = station_confidence_table(
13    "data/AMT/WILLY_DATA/L18PLT",
14    method="composite",
15    weights=weights,
16    relerr_threshold=0.25,
17    offdiag_tolerance_log10=0.40,
18    diagonal_leakage_max=0.40,
19    phase_jump_tolerance_deg=90.0,
20    spatial_tolerance_log10=0.60,
21    api=False,
22)
23
24print(table.sort_values("confidence").head())
    station  distance_m  confidence  ...  diagonal     phase   spatial
22  18-022U      4400.0    0.630495  ...  0.000000  0.941638  0.000000
21  18-021U      4200.0    0.659512  ...  0.000000  0.938165  0.000000
17  18-018A      3400.0    0.666682  ...  0.201107  0.957546  0.000000
16  18-017U      3200.0    0.672222  ...  0.353986  0.969783  0.038160
14  18-015U      2800.0    0.708737  ...  0.514479  0.965328  0.575751

[5 rows x 13 columns]

Changing thresholds changes the meaning of the scores. Record custom weights and thresholds in reports so another user can reproduce your confidence classes.

Frequency-Level Confidence#

frequency_confidence_table returns one row per station-frequency sample. This is the table to use for period-band decisions, masks, and inversion down-weighting.

 1from pycsamt.emtools import frequency_confidence_table
 2
 3freq_qc = frequency_confidence_table(
 4    "data/AMT/WILLY_DATA/L18PLT",
 5    method="composite",
 6    ci_hi=0.95,
 7    ci_lo=0.85,
 8    api=False,
 9)
10
11print(freq_qc.columns.tolist())
12print(freq_qc[["station", "frequency_hz", "period_s",
13               "confidence", "flags"]].head())
14
15rejected = freq_qc[freq_qc["flags"].str.contains("reject", na=False)]
16print("rejected cells:", len(rejected))
['station', 'station_index', 'distance_m', 'frequency_hz', 'period_s', 'log10_period', 'confidence', 'confidence_err', 'method', 'n_components', 'coverage', 'uncertainty', 'offdiag', 'diagonal', 'phase', 'spatial', 'logrho_proxy', 'flags']
   station  ...                                              flags
0  18-001A  ...  recoverable,high_error,offdiag_mismatch,diagon...
1  18-001A  ...  reject,high_error,offdiag_mismatch,diagonal_le...
2  18-001A  ...  reject,high_error,offdiag_mismatch,diagonal_le...
3  18-001A  ...  reject,high_error,offdiag_mismatch,diagonal_le...
4  18-001A  ...  reject,high_error,offdiag_mismatch,diagonal_le...

[5 rows x 5 columns]
rejected cells: 1479

Frequency flags can include reject, recoverable, missing, high_error, offdiag_mismatch, diagonal_leakage, phase_jump, and spatial_outlier.

Build A Mask From Confidence#

The QC module does not force one masking policy. You can build one from the confidence table.

 1from pycsamt.emtools import frequency_confidence_table
 2
 3table = frequency_confidence_table(
 4    "data/AMT/WILLY_DATA/L18PLT",
 5    method="composite",
 6    ci_lo=0.85,
 7    api=False,
 8)
 9
10keep = table["confidence"] >= 0.85
11review = (table["confidence"] >= 0.70) & (table["confidence"] < 0.85)
12drop = table["confidence"] < 0.70
13
14print("keep:", int(keep.sum()))
15print("review:", int(review.sum()))
16print("drop:", int(drop.sum()))
keep: 5
review: 528
drop: 951

Use a review band instead of a hard delete when the flagged frequencies line up with known structural complexity. Low confidence is a prompt for inspection, not always proof of bad data.

Station Confidence Profile#

plot_confidence_profile plots station confidence along the line. It uses green, pink, and red markers for safe, recoverable, and rejected stations.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_confidence_profile
 4
 5fig, ax = plt.subplots(figsize=(9.5, 4.2))
 6plot_confidence_profile(
 7    "data/AMT/WILLY_DATA/L18PLT",
 8    method="composite",
 9    ci_hi=0.95,
10    ci_lo=0.85,
11    shade_mode="score",
12    station_label_step=2,
13    show_errorbars=True,
14    ax=ax,
15)
16
17fig.tight_layout()
18fig.savefig("confidence_profile_l18plt.png", dpi=200)
19plt.close(fig)
../../_images/user-guide-emtools-qc-09.png

If no station coordinate metadata are available, distance falls back to regular spacing controlled by spacing_m.

Frequency Confidence Pseudo-Section#

plot_frequency_confidence_psection shows confidence by station and period. It can plot any metric column from the frequency table, not only confidence.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_frequency_confidence_psection
 4
 5fig, ax = plt.subplots(figsize=(10.0, 4.8))
 6plot_frequency_confidence_psection(
 7    "data/AMT/WILLY_DATA/L18PLT",
 8    method="composite",
 9    metric="confidence",
10    station_label_step=2,
11    ax=ax,
12)
13
14fig.savefig("frequency_confidence_psection.png", dpi=200)
15plt.close(fig)
../../_images/user-guide-emtools-qc-10.png

Change metric to "uncertainty", "offdiag", "diagonal", "phase", or "spatial" to see which component is driving low confidence.

Single-Station Spectrum#

plot_station_confidence_spectrum overlays the overall confidence curve and component scores for one station.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_station_confidence_spectrum
 4
 5fig, ax = plt.subplots(figsize=(7.5, 4.2))
 6plot_station_confidence_spectrum(
 7    "data/AMT/WILLY_DATA/L18PLT",
 8    station="18-022U",
 9    method="composite",
10    ax=ax,
11)
12
13fig.savefig("station_confidence_spectrum_18-022U.png", dpi=200)
14plt.close(fig)
../../_images/user-guide-emtools-qc-11.png

Use this plot when you know a station is weak and want to see whether the problem comes from uncertainty, diagonal leakage, off-diagonal mismatch, phase jumps, or spatial incoherence.

Single-Station Dashboard#

plot_station_confidence_dashboard breaks the same information into separate panels. It is easier to read than the overlaid spectrum when many score components overlap.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_station_confidence_dashboard
 4
 5fig = plot_station_confidence_dashboard(
 6    "data/AMT/WILLY_DATA/L18PLT",
 7    station="18-022U",
 8    method="composite",
 9    ci_hi=0.95,
10    ci_lo=0.85,
11    figsize=(11.0, 7.0),
12)
13
14fig.savefig("station_confidence_dashboard_18-022U.png", dpi=200)
15plt.close(fig)
../../_images/user-guide-emtools-qc-12.png

Use dashboards for station-by-station review before deciding whether a low-confidence station should be edited, down-weighted, or retained.

Period-Band Summary#

plot_confidence_band_summary collapses the frequency table by period. It plots median and mean confidence and shades the fraction of stations in rejected or recoverable bands.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools import plot_confidence_band_summary
 4
 5fig, ax = plt.subplots(figsize=(8.5, 4.2))
 6plot_confidence_band_summary(
 7    "data/AMT/WILLY_DATA/L18PLT",
 8    method="composite",
 9    ci_hi=0.95,
10    ci_lo=0.85,
11    ax=ax,
12)
13
14fig.savefig("confidence_band_summary.png", dpi=200)
15plt.close(fig)
../../_images/user-guide-emtools-qc-13.png

This view is useful when deciding whether a whole period band should be edited, down-weighted, or treated cautiously.

Coverage And SNR Quicklook#

plot_qc_quicklook combines three first-pass plots: a presence pseudo-section, an SNR pseudo-section, and an SNR histogram.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools.qc import plot_qc_quicklook
 4
 5fig = plot_qc_quicklook(
 6    "data/AMT/WILLY_DATA/L18PLT",
 7    figsize=(10.0, 8.0),
 8)
 9
10fig.savefig("qc_quicklook_l18plt.png", dpi=200)
11plt.close(fig)
../../_images/user-guide-emtools-qc-14.png

If the SNR histogram says error tensors are not available, the survey can still be inspected, but uncertainty-based scores will be missing from the composite confidence ratio.

Coverage Pseudo-Section And SNR Histogram#

For more control, call the helper plots separately.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools.qc import plot_coverage_psection, plot_snr_hist
 4
 5fig, axes = plt.subplots(1, 2, figsize=(12.0, 4.5))
 6
 7plot_coverage_psection(
 8    "data/AMT/WILLY_DATA/L18PLT",
 9    metric="presence",
10    ax=axes[0],
11)
12axes[0].set_title("Finite-row presence")
13
14plot_snr_hist(
15    "data/AMT/WILLY_DATA/L18PLT",
16    bins=40,
17    ax=axes[1],
18)
19axes[1].set_title("Row SNR distribution")
20
21fig.tight_layout()
22fig.savefig("coverage_and_snr.png", dpi=200)
23plt.close(fig)
../../_images/user-guide-emtools-qc-15.png

metric="presence" shows finite rows. metric="snr" colours by row SNR when z_err exists. metric="offdiag" shows an off-diagonal amplitude proxy.

Consistency Fan#

plot_consistency_fan propagates impedance errors through apparent resistivity by Monte Carlo sampling. It can compare xy and yx apparent-resistivity bands for one station.

 1import matplotlib.pyplot as plt
 2import numpy as np
 3
 4from pycsamt.emtools.qc import overlay_noise_cone, plot_consistency_fan
 5
 6fig, ax = plt.subplots(figsize=(8.8, 4.5))
 7plot_consistency_fan(
 8    "data/AMT/WILLY_DATA/L18PLT",
 9    station="18-016A",
10    comps=("xy", "yx"),
11    pcts=(10.0, 50.0, 90.0),
12    n_draws=300,
13    ax=ax,
14)
15ax.set_yscale("log")
16
17period = np.logspace(-4, 0, 30)
18overlay_noise_cone(
19    ax,
20    period,
21    lo=np.full(period.size, 10.0),
22    hi=np.full(period.size, 100.0),
23    color="0.5",
24    alpha=0.20,
25)
26
27fig.savefig("consistency_fan_18-016A.png", dpi=200)
28plt.close(fig)
../../_images/user-guide-emtools-qc-16.png

The noise cone overlay is a visual reference band. It is not estimated by the QC module. Supply project-specific lower and upper bounds when you use it in a report.

XY/YX Crossover Map#

plot_xyyx_crossover_map marks periods where rho_xy and rho_yx swap which one is larger. It is a cheap anisotropy and mode-consistency diagnostic.

 1import matplotlib.pyplot as plt
 2
 3from pycsamt.emtools.qc import (
 4    overlay_spectral_holes,
 5    plot_xyyx_crossover_map,
 6)
 7
 8fig, ax = plt.subplots(figsize=(9.5, 4.8))
 9plot_xyyx_crossover_map(
10    "data/AMT/WILLY_DATA/L18PLT",
11    ax=ax,
12)
13overlay_spectral_holes(
14    ax,
15    "data/AMT/WILLY_DATA/L18PLT",
16    thresh_dec=0.30,
17)
18
19fig.savefig("xy_yx_crossover_map.png", dpi=200)
20plt.close(fig)
../../_images/user-guide-emtools-qc-17.png

overlay_spectral_holes shades large gaps in log-period sampling on top of a pseudo-section-style axis. Lower thresh_dec only when you intentionally want to reveal small grid spacing differences.

Propagation To Inversion#

For MARE2DEM exports created from EDI data, CR-derived uncertainty propagation can be enabled with:

1from pycsamt.models.mare2dem.edi import make_mt_data_from_edi
2
3make_mt_data_from_edi(
4    "data/AMT/WILLY_DATA/L18PLT",
5    "mare2dem_data_with_confidence.emdata",
6    confidence_weighting=True,
7)

The effective relative impedance error is inflated as confidence decreases:

\[\epsilon_{Z,\mathrm{eff}} = \epsilon_Z \left[{1 \over \max(\mathrm{CR}, \mathrm{CR}_{\min})}\right]^p .\]

The defaults are CR_min=0.05 and p=1. The usual propagation is then:

\[\sigma_{\rho_a,\mathrm{eff}} = 2\rho_a\,\epsilon_{Z,\mathrm{eff}}, \qquad \sigma_{\phi,\mathrm{eff}} = {180 \over \pi}\epsilon_{Z,\mathrm{eff}} .\]

Confidence weighting should increase uncertainty for low-confidence data. It should not make any datum artificially more precise.

Build A QC Report Bundle#

The following script writes station tables, frequency tables, and the main QC figures for one line.

 1from pathlib import Path
 2
 3import matplotlib.pyplot as plt
 4
 5from pycsamt.emtools import (
 6    build_qc_table,
 7    ensure_sites,
 8    frequency_confidence_table,
 9    plot_confidence_band_summary,
10    plot_confidence_profile,
11    plot_frequency_confidence_psection,
12    qc_flags,
13    station_confidence_table,
14)
15from pycsamt.emtools.qc import plot_qc_quicklook
16
17out = Path("qc_report_l18plt")
18out.mkdir(parents=True, exist_ok=True)
19
20survey = ensure_sites("data/AMT/WILLY_DATA/L18PLT", strict=True)
21
22build_qc_table(survey, api=False).to_csv(
23    out / "station_qc_summary.csv",
24    index=False,
25)
26qc_flags(survey).to_csv(out / "station_qc_flags.csv", index=False)
27station_confidence_table(survey, method="composite", api=False).to_csv(
28    out / "station_confidence.csv",
29    index=False,
30)
31frequency_confidence_table(survey, method="composite", api=False).to_csv(
32    out / "frequency_confidence.csv",
33    index=False,
34)
35
36fig, ax = plt.subplots(figsize=(9.5, 4.2))
37plot_confidence_profile(survey, method="composite", ax=ax)
38fig.tight_layout()
39fig.savefig(out / "confidence_profile.png", dpi=200)
40plt.close(fig)
41
42fig, ax = plt.subplots(figsize=(10.0, 4.8))
43plot_frequency_confidence_psection(survey, method="composite", ax=ax)
44fig.savefig(out / "frequency_confidence_psection.png", dpi=200)
45plt.close(fig)
46
47fig, ax = plt.subplots(figsize=(8.5, 4.2))
48plot_confidence_band_summary(survey, method="composite", ax=ax)
49fig.savefig(out / "confidence_band_summary.png", dpi=200)
50plt.close(fig)
51
52fig = plot_qc_quicklook(survey)
53fig.savefig(out / "qc_quicklook.png", dpi=200)
54plt.close(fig)

Reading QC Results#

Use QC scores as evidence, not as an automatic delete button.

coverage is low

The station or frequency is genuinely incomplete. Investigate loading, editing, or acquisition gaps.

uncertainty is low

Error tensors are large relative to impedance magnitude. This is a strong reason to down-weight or review.

offdiag is low

Zxy and Zyx amplitudes disagree beyond the selected tolerance. Compare with anisotropy, impedance, and tensor tools.

diagonal is low

Diagonal leakage is high. Check dimensionality and coordinate orientation before calling it acquisition noise.

phase is low

Off-diagonal phase changes abruptly with frequency. Check frequency editing and processing history.

spatial is low

The station differs from neighbouring stations at the same frequency or in median response. Check station metadata and local geology.

Worked Example#

The gallery example applies the station, frequency, profile, dashboard, quicklook, fan, crossover, and hole-overlay workflows to the bundled L18PLT survey.

Open the rendered gallery page here: Quality-control confidence scoring (pycsamt.emtools.qc).