Coverage for C: \ Users \ peaco \ OneDrive \ Documents \ GitHub \ mth5 \ mth5 \ tables \ tf_table.py: 96%
54 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-27 20:09 -0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-27 20:09 -0800
1# -*- coding: utf-8 -*-
2"""
3Transfer function summary table utilities.
5Summarize `TransferFunction` groups stored in an MTH5 file into a structured
6table and provide a convenient `pandas.DataFrame` view for querying.
8Notes
9-----
10- Traversal searches for groups with attribute ``mth5_type='transferfunction'``
11 and collects basic availability flags (impedance, tipper, covariance) along
12 with period range and references.
14"""
16from __future__ import annotations
18import h5py
19import numpy as np
21# =============================================================================
22# Imports
23# =============================================================================
24import pandas as pd
26from mth5 import TF_DTYPE
27from mth5.helpers import validate_name
28from mth5.tables import MTH5Table
31# =============================================================================
34class TFSummaryTable(MTH5Table):
35 """
36 Summary table for `TransferFunction` groups.
38 Provides convenience functions to populate the table (`summarize`) and
39 export to `pandas.DataFrame` (`to_dataframe`).
41 Examples
42 --------
43 Build and export a TF summary::
45 >>> import h5py
46 >>> from mth5.tables.tf_table import TFSummaryTable
47 >>> f = h5py.File('example.mth5', 'r')
48 >>> tf_summary_ds = f['Exchange']['TF_Summary']
49 >>> tf_table = TFSummaryTable(tf_summary_ds)
50 >>> tf_table.summarize()
51 >>> df = tf_table.to_dataframe()
52 >>> df.head()
53 """
55 def __init__(self, hdf5_dataset: h5py.Dataset) -> None:
56 super().__init__(hdf5_dataset, TF_DTYPE)
58 def to_dataframe(self) -> pd.DataFrame:
59 """
60 Convert the table to a `pandas.DataFrame` for easier querying.
62 Returns
63 -------
64 pandas.DataFrame
65 A dataframe with decoded string columns.
67 Examples
68 --------
69 Filter transfer functions that include tipper::
71 >>> df = tf_table.to_dataframe()
72 >>> df[df.has_tipper]
73 """
75 df = pd.DataFrame(self.array[()])
76 for key in [
77 "station",
78 "survey",
79 "tf_id",
80 "units",
81 ]:
82 setattr(df, key, getattr(df, key).str.decode("utf-8"))
83 return df
85 def summarize(self) -> None:
86 """
87 Populate the summary table by traversing the HDF5 hierarchy.
89 Searches for groups where ``mth5_type`` equals ``'transferfunction'``
90 and adds a row indicating available datasets (impedance, tipper,
91 covariance), period min/max, and relevant references.
93 Returns
94 -------
95 None
97 Examples
98 --------
99 Refresh the TF summary::
101 >>> tf_table.clear_table()
102 >>> tf_table.summarize()
103 """
104 self.clear_table()
106 def recursive_get_tf_entry(
107 group: h5py.Group | h5py.File | h5py.Dataset,
108 ) -> None:
109 """Recursively collect TF summary entries from the hierarchy."""
110 if isinstance(group, (h5py.Group, h5py.File)):
111 for key, node in group.items():
112 try:
113 group_type = node.attrs["mth5_type"].lower()
114 if group_type == "transferfunction":
115 has_impedance = False
116 has_tipper = False
117 has_covariance = False
118 if "transfer_function" in node.keys():
119 tf_dataset = node["transfer_function"]
120 if tf_dataset != (1, 1, 1):
121 nz = np.nonzero(tf_dataset)
122 unique_values = np.unique(nz[1])
123 if 0 in unique_values or 1 in unique_values:
124 has_impedance = True
125 if 2 in unique_values:
126 has_tipper = True
127 if (
128 "residual_covariance" in node.keys()
129 and "inverse_signal_power" in node.keys()
130 ):
131 res = node["residual_covariance"]
132 isp = node["inverse_signal_power"]
134 if res.shape != (1, 1, 1) and isp.shape != (
135 1,
136 1,
137 1,
138 ):
139 has_covariance = True
140 if "period" in node.keys():
141 period = node["period"][()]
142 else:
143 period = np.zeros(2)
144 tf_entry = np.array(
145 [
146 (
147 validate_name(
148 node.parent.parent.attrs["id"]
149 ).encode("utf-8"),
150 validate_name(
151 node.parent.parent.parent.parent.attrs["id"]
152 ).encode("utf-8"),
153 node.parent.parent.attrs["location.latitude"],
154 node.parent.parent.attrs["location.longitude"],
155 node.parent.parent.attrs["location.elevation"],
156 validate_name(node.attrs["id"]).encode("utf-8"),
157 node.attrs["units"],
158 has_impedance,
159 has_tipper,
160 has_covariance,
161 period.min(),
162 period.max(),
163 node.ref,
164 node.parent.parent.ref,
165 )
166 ],
167 dtype=TF_DTYPE,
168 )
169 self.add_row(tf_entry)
170 else:
171 recursive_get_tf_entry(node)
172 except KeyError:
173 recursive_get_tf_entry(node)
174 elif isinstance(group, h5py.Dataset):
175 pass
177 parent = self.array.parent
178 # Allow Mock objects and dictionaries for testing, in addition to h5py types
179 if not (
180 isinstance(parent, (h5py.Group, h5py.File))
181 or hasattr(parent, "items")
182 or isinstance(parent, dict)
183 ):
184 raise TypeError("Unexpected parent type for summary dataset.")
185 recursive_get_tf_entry(parent)